⚖️ Старый vs Новый подход

Устаревшие паттерны из лекции — современные аналоги на DRF 3.15+ / Django 5.x

⚡ Основные изменения

  • drf-yasg (OpenAPI 2.0)drf-spectacular (OpenAPI 3.0) — современный стандарт
  • Ручная проверка user в viewBasePermission с has_object_permission — DRF-способ
  • dumpdata без флаговdumpdata --exclude contenttypes --exclude auth.permission — безопаснее для восстановления
  • router.register + as_view() → зависит от типа ViewSet: ModelViewSet регистрируется только через router

1. Документация API: drf-yasg vs drf-spectacular

Из лекции (drf-yasg)

# pip install drf-yasg
# OpenAPI 2.0 (Swagger)
INSTALLED_APPS = ['drf_yasg']

from drf_yasg.views import get_schema_view
from drf_yasg import openapi

schema_view = get_schema_view(
    openapi.Info(title="API", default_version='v1'),
    public=True,
)
# /swagger/ и /redoc/

Современный (drf-spectacular)

# pip install drf-spectacular
# OpenAPI 3.0 (актуальный стандарт)
INSTALLED_APPS = ['drf_spectacular']

REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS':
        'drf_spectacular.openapi.AutoSchema',
}

# urls.py
from drf_spectacular.views import (
    SpectacularAPIView,
    SpectacularSwaggerView,
)
urlpatterns += [
    path('api/schema/', SpectacularAPIView.as_view()),
    path('api/docs/', SpectacularSwaggerView.as_view()),
]
Рекомендация: В данном практикуме используется drf-yasg согласно источнику лекции. Для новых проектов предпочтительнее drf-spectacular с поддержкой OpenAPI 3.0. ⚠️ Проверить по документации: совместимость зависит от версии DRF и Django.

2. Проверка прав: ручная vs BasePermission

Устаревший (ручная проверка в view)

# Антипаттерн: логика прав в самом view
def update(self, request, pk=None):
    order = Order.objects.get(pk=pk)
    if order.customer != request.user:
        return Response(
            {'error': 'Forbidden'},
            status=403
        )
    # ... логика обновления

Современный (BasePermission)

# permissions.py — переиспользуемо
class IsCustomerOrReadOnly(BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in ['GET', 'HEAD', 'OPTIONS']:
            return True
        return obj.customer == request.user

# view: чистая логика
class OrderDetailView(RetrieveUpdateDestroyAPIView):
    permission_classes = [IsCustomerOrReadOnly]
    queryset = Order.objects.all()
    serializer_class = OrderSerializer

3. dumpdata: простой vs безопасный

Из лекции (базовый)

# Дамп всего
python manage.py dumpdata \
  --indent=4 > db_backup.json

# Восстановление
python manage.py migrate
python manage.py loaddata db_backup.json

Современный (безопаснее для loaddata)

# Исключить проблемные таблицы
python manage.py dumpdata \
  --exclude auth.permission \
  --exclude contenttypes \
  --indent=4 > db_backup.json

# Восстановление без конфликтов
python manage.py migrate
python manage.py loaddata db_backup.json
ContentType-конфликты возникают при восстановлении на другой БД или при изменении структуры приложений. Флаг --exclude contenttypes помогает избежать этой проблемы. ⚠️ Проверить по документации: поведение зависит от структуры проекта и версии Django.

4. ModelViewSet: router vs as_view()

Из лекции (ModelViewSet с path)

# В лекции ModelViewSet
# регистрируется через path с as_view():
path('orders/', OrderListCreateView.as_view(),
     name='orders'),
# Это работает только если OrderListCreateView
# НЕ является ModelViewSet

Правильно для ModelViewSet

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'orders', OrderListCreateView,
                basename='order')

urlpatterns = [
    path('', include(router.urls)),
]

# Или через as_view() с маппингом:
path('orders/', OrderListCreateView.as_view({
    'get': 'list',
    'post': 'create'
}), name='orders'),
← К оглавлению урока    Задания →