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 з полями name і biography.
  2. Додайте кастомний маршрут /authors/top/, який повертає авторів з найбільшою кількістю книг.
  3. Налаштуйте свій роутер, щоб замінити маршрут /authors/ на /writers/.

Переходьте до реалізації — і нехай сила DRF та натхнення будуть з вами!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ