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

🎯 Из лекции (старое) → современное К оглавлению урока

⚡ Главные отличия

  • Ручной CRUD в APIView → Generic Views / ModelViewSet
  • Manual URL patterns → Router.register()
  • Физическое удаление → Soft Delete (is_deleted + Manager)
  • transaction.set_autocommit(False) → with transaction.atomic()
  • Пагинация вручную (срезы QuerySet) → pagination_class
  • DEBUG-флаг для SQL логов → LOGGING с django.db.backends

1. Ручной CRUD vs Generic Views

Из лекции (старое) — ручная реализация всех методов в GenericAPIView напрямую:

# Старый подход — вручную писать каждый метод
class BookListView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        books = self.get_queryset()
        serializer = self.get_serializer(books, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)

Современный подход — готовый класс реализует методы автоматически:

# Современный — ListCreateAPIView делает всё то же самое
class BookListCreateView(ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # get() и post() реализованы внутри миксинов

2. Ручная маршрутизация vs Router

Старый подход — вручную указывать все URL для ViewSet:

# Старый — дублирование URL для каждого метода
from .views import GenreViewSet

genre_list = GenreViewSet.as_view({'get': 'list', 'post': 'create'})
genre_detail = GenreViewSet.as_view({
    'get': 'retrieve', 'put': 'update',
    'patch': 'partial_update', 'delete': 'destroy'
})

urlpatterns = [
    path('genres/', genre_list),
    path('genres/<int:pk>/', genre_detail),
]

Современный подход — Router генерирует маршруты автоматически:

# Современный — 3 строки вместо 8
from rest_framework.routers import DefaultRouter
from .views import GenreViewSet

router = DefaultRouter()
router.register(r'genres', GenreViewSet)
urlpatterns = [path('', include(router.urls))]

3. Физическое удаление vs Soft Deletion

Старый подход — DELETE удаляет запись из БД навсегда:

# Старый — физическое удаление
def destroy(self, request, *args, **kwargs):
    instance = self.get_object()
    instance.delete()  # запись пропадёт из БД
    return Response(status=status.HTTP_204_NO_CONTENT)

Современный подход — мягкое удаление с флагом:

# Современный — Soft Delete
class Category(models.Model):
    is_deleted = models.BooleanField(default=False)
    deleted_at = models.DateTimeField(null=True, blank=True)
    objects = SoftDeleteManager()

    def delete(self, *args, **kwargs):
        self.is_deleted = True
        self.deleted_at = timezone.now()
        self.save()  # запись сохраняется, только помечается

4. Ручное управление транзакциями vs transaction.atomic

Старый подход из лекции — ручное управление autocommit:

# Старый — явное управление через set_autocommit
transaction.set_autocommit(False)
try:
    publisher = Publisher.objects.create(...)
    book = Book.objects.create(publisher=publisher, ...)
    transaction.commit()
except Exception:
    transaction.rollback()
finally:
    transaction.set_autocommit(True)

Современный подход — контекстный менеджер atomic():

# Современный — transaction.atomic() предпочтителен
with transaction.atomic():
    publisher = Publisher.objects.create(...)
    book = Book.objects.create(publisher=publisher, ...)
# Откат произойдёт автоматически при любом исключении внутри блока

5. Ручная пагинация через срезы vs pagination_class

Старый подход — срезы QuerySet напрямую:

# Старый — пагинация вручную
page = int(request.query_params.get('page', 1))
page_size = int(request.query_params.get('page_size', 10))
start = (page - 1) * page_size
end = start + page_size
queryset = Book.objects.all()[start:end]
# нет метаданных (next/previous/count)

Современный подход — класс пагинации с метаданными:

# Современный — PageNumberPagination
class BookPagination(PageNumberPagination):
    page_size = 10
    page_size_query_param = 'page_size'
    max_page_size = 100

class BookListView(ListAPIView):
    pagination_class = BookPagination
    # Ответ включает: count, next, previous, results

6. print() для отладки SQL vs LOGGING

Старый подход — вывод SQL вручную через django.db.connection:

# Старый — ручной вывод запросов
from django.db import connection

def some_view(request):
    books = Book.objects.all()
    print(connection.queries)  # только в коде, только одно место

Современный подход — LOGGING записывает всё автоматически:

# Современный — централизованное логирование в settings.py
LOGGING = {
    'version': 1,
    'loggers': {
        'django.db.backends': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
        },
    },
}
# Все SQL-запросы пишутся в файл + консоль