⚖️ Старый 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-запросы пишутся в файл + консоль