JavaRush /Курсы /Модуль 3: Django /Кастомизация маршрутов в Routers

Кастомизация маршрутов в Routers

Модуль 3: Django
20 уровень , 7 лекция
Открыта

Пора углубиться в кастомизацию маршрутов! Потому что иногда стандартные подходы — это прекрасно, но уникальность проекта требует нестандартных решений.

Зачем кастомизировать маршруты?

Роутеры в DRF удобны, но они создают маршруты по стандартным правилам. Например, для ViewSet с названием UserViewSet роутеры автоматически добавят CRUD-маршруты по шаблону /users/.

Но что, если:

  • Вам нужно изменить URL, чтобы он был более читабельным и подходил под специфику проекта, например, /accounts/ вместо /users/.
  • Нужно добавить кастомное поведение, например, маршрут для массового экспорта данных /users/export/.
  • Вы хотите скрыть некоторые стандартные CRUD-операции, например, запретить удаление пользователей через API.

Вот тут и вступает в игру кастомизация маршрутов.

Как кастомизировать маршруты: теория и основные подходы

В DRF существует несколько подходов к кастомизации маршрутов:

  1. Переопределение стандартных маршрутов
    Меняем URL или используем свои имена маршрутов.

  2. Добавление новых маршрутов через кастомные действия (actions)
    Добавляем новые эндпоинты к уже существующим ViewSet.

  3. Создание полностью специализированных роутеров
    Если вы хотите пойти дальше и разработать свою логику маршрутизации, DRF позволяет создать кастомные роутеры.

Кастомизация маршрутов: простые примеры

Давайте сразу перейдём к практике. Мы будем работать с проектом, в котором есть модель Book и ViewSet для неё. Вот краткий контекст:

Модель Book

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.CharField(max_length=255)
    published_date = models.DateField()
    isbn = models.CharField(max_length=13, unique=True)

ViewSet для работы с книгами

from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializers import BookSerializer

class BookViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

Подключение маршрутов через DefaultRouter

from rest_framework.routers import DefaultRouter
from .views import BookViewSet

router = DefaultRouter()
router.register(r'books', BookViewSet, basename='books')

urlpatterns = router.urls

Теперь у нас есть стандартные маршруты:

GET     /books/         # Список всех книг
POST    /books/         # Добавление новой книги
GET     /books/{id}/    # Детали конкретной книги
PUT     /books/{id}/    # Обновление книги
DELETE  /books/{id}/    # Удаление книги
  1. Переопределение стандартных маршрутов

Предположим, вы хотите заменить /books/ на /library/books/. Для этого вам нужно указать кастомный prefix при регистрации ViewSet:

router.register(r'library/books', BookViewSet, basename='library-books')

Теперь маршруты будут выглядеть так:

GET     /library/books/         # Список всех книг
POST    /library/books/         # Добавление новой книги
GET     /library/books/{id}/    # Детали конкретной книги
PUT     /library/books/{id}/    # Обновление книги
DELETE  /library/books/{id}/    # Удаление книги
  1. Добавление новых маршрутов (actions)

Допустим, нам нужно добавить новый маршрут /books/latest/, который будет возвращать последние добавленные книги. В DRF для добавления таких маршрутов используется декоратор @action внутри ViewSet.

Пример:

from rest_framework.decorators import action
from rest_framework.response import Response

class BookViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    @action(detail=False, methods=['get'], url_path='latest')
    def latest_books(self, request):
        """Возвращает последние 5 добавленных книг"""
        latest_books = self.queryset.order_by('-published_date')[:5]
        serializer = self.get_serializer(latest_books, many=True)
        return Response(serializer.data)

Мы добавили кастомное действие latest_books. Теперь маршрут автоматически подключен к роутеру:

GET     /books/latest/          # Возвращает последние добавленные книги
  • detail=False означает, что маршрут не требует ID объекта (он относится к коллекции).
  • methods=['get'] задаёт HTTP-методы, которые обрабатывает маршрут.
  • url_path='latest' позволяет настроить путь. По умолчанию будет использовано имя функции.
  1. Кастомизация стандартных операций

Что, если мы хотим изменить, как работает стандартный маршрут /books/{id}/? Например, добавить в него дополнительную проверку или изменить ответ.

Всё, что нужно сделать, — переопределить стандартный метод ViewSet, например, retrieve:

class BookViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def retrieve(self, request, *args, **kwargs):
        """Добавляем дополнительное поведение при получении объекта"""
        instance = self.get_object()
        data = self.get_serializer(instance).data
        data['message'] = 'Это ваши данные по книге!'
        return Response(data)

Теперь при запросе GET /books/{id}/ в ответе будет дополнительное поле message.

  1. Создание кастомного роутера

Иногда стандартные роутеры (например, DefaultRouter) оказываются слишком ограниченными. В таких случаях можно написать свой роутер, который будет генерировать маршруты по вашим правилам.

Вот пример минимального кастомного роутера:

from rest_framework.routers import Route, SimpleRouter

class CustomRouter(SimpleRouter):
    routes = [
        Route(
            url=r'^{prefix}/custom/$',  # Определяем наш URL
            mapping={'get': 'custom'}, # Определяем HTTP-методы и соответствующие им действия
            name='{basename}-custom',
            detail=False,
            initkwargs={'suffix': 'Custom'}
        ),
    ]

Теперь можно подключить этот роутер к нашему ViewSet. К примеру, захотим добавить новый маршрут /books/custom/:

# В BookViewSet нужно определить метод custom
class BookViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def custom(self, request):
        """Пример для кастомного метода"""
        return Response({'message': 'Это кастомный маршрут!'})

# Используем наш роутер
router = CustomRouter()
router.register(r'books', BookViewSet, basename='books')

urlpatterns = router.urls

Типичные проблемы и как их избежать

  1. Конфликты маршрутов
    Если вы добавляете кастомные действия или маршруты, убедитесь, что их пути не пересекаются с существующими маршрутами.

  2. Потеря семантики
    Старайтесь выбирать такие URL и названия действий, которые описывают их назначение. Например, /books/latest/ вместо /books/abc/.

  3. Избыточная кастомизация
    Не переусердствуйте: DRF уже хорошо автоматизирует рутину. Используйте кастомизацию только в случаях, когда это действительно необходимо.

Практическое задание

  1. Создайте ViewSet для модели Author c полями name и biography.
  2. Добавьте кастомный маршрут /authors/top/, который возвращает авторов с наибольшим количеством книг.
  3. Настройте свой роутер, чтобы заменить маршрут /authors/ на /writers/.

Переходите к реализации — и да пребудет с вами сила DRF и вдохновение!

1
Задача
Модуль 3: Django, 20 уровень, 7 лекция
Недоступна
Добавление кастомного маршрута
Добавление кастомного маршрута
1
Задача
Модуль 3: Django, 20 уровень, 7 лекция
Недоступна
Кастомизация маршрута с дополнительными параметрами
Кастомизация маршрута с дополнительными параметрами
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ