Час заглибитись у кастомізацію маршрутів! Бо іноді стандартні підходи — це чудово, але унікальність проєкту вимагає нестандартних рішень.
Навіщо кастомізувати маршрути?
Роутери в DRF зручні, але вони створюють маршрути за стандартними правилами. Наприклад, для ViewSet з назвою UserViewSet роутери автоматично додадуть CRUD-маршрути за шаблоном /users/.
Але що, якщо:
- Вам потрібно змінити URL, щоб він був більш читабельним і підходив під специфіку проєкту, наприклад,
/accounts/замість/users/. - Потрібно додати кастомну поведінку, наприклад, маршрут для масового експорту даних
/users/export/. - Ви хочете приховати деякі стандартні CRUD-операції, наприклад, заборонити видалення користувачів через API.
Ось тут і вступає в гру кастомізація маршрутів.
Як кастомізувати маршрути: теорія та основні підходи
У DRF існує кілька підходів до кастомізації маршрутів:
Перевизначення стандартних маршрутів
Змінюємо URL або використовуємо свої імена маршрутів.Додавання нових маршрутів через кастомні дії (actions)
Додаємо нові ендпоінти до вже існуючих ViewSet.Створення повністю спеціалізованих роутерів
Якщо хочете піти далі і розробити свою логіку маршрутизації, 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}/ # Видалення книги
- Перевизначення стандартних маршрутів
Припустимо, ви хочете замінити /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}/ # Видалення книги
- Додавання нових маршрутів (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'дозволяє налаштувати шлях. За замовчуванням буде використано ім'я функції.
- Кастомізація стандартних операцій
Що, якщо ми хочемо змінити, як працює стандартний маршрут /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.
- Створення кастомного роутера
Іноді стандартні роутери (наприклад, 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
Типові проблеми та як їх уникнути
Конфлікти маршрутів
Якщо додаєте кастомні дії або маршрути, переконайтеся, що їх шляхи не перетинаються з існуючими маршрутами.Втрата семантики
Намагайтеся обирати такі URL і назви дій, які описують їх призначення. Наприклад,/books/latest/замість/books/abc/.Надмірна кастомізація
Не перестарайтеся: DRF вже добре автоматизує рутину. Використовуйте кастомізацію тільки в тих випадках, коли це дійсно необхідно.
Практичне завдання
- Створіть ViewSet для моделі
Authorз полямиnameіbiography. - Додайте кастомний маршрут
/authors/top/, який повертає авторів з найбільшою кількістю книг. - Налаштуйте свій роутер, щоб замінити маршрут
/authors/на/writers/.
Переходьте до реалізації — і нехай сила DRF та натхнення будуть з вами!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ