⚖️ Старый vs Новый — Урок 38

← К оглавлению урока

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

  • Ручная пагинация в APIView → pagination_class в Generic Views
  • Устаревший DRF-формат LOGGING с '%(levelname)s''style': '{' в Django 3.2+
  • Глобальная пагинация без кастомного класса → всегда кастомный класс для CursorPagination

1. Ручная пагинация (из лекции) → pagination_class (современно)

Из лекции (старое) — ручная пагинация в APIView

# views.py — ручная пагинация через Paginator (Django встроенный)
from django.core.paginator import Paginator
from rest_framework.views import APIView
from rest_framework.response import Response

class BookListView(APIView):
    def get(self, request):
        books = Book.objects.all()
        paginator = Paginator(books, 5)       # 5 на страницу
        page_number = request.query_params.get('page', 1)
        page_obj = paginator.get_page(page_number)
        serializer = BookSerializer(page_obj, many=True)
        return Response({
            'count': paginator.count,
            'results': serializer.data,
        })

Современное — pagination_class в Generic Views (DRF 3.x)

# views.py — DRF встроенная пагинация
from rest_framework.generics import ListAPIView
from rest_framework.pagination import PageNumberPagination

class BookPagination(PageNumberPagination):
    page_size = 5
    page_size_query_param = 'page_size'
    max_page_size = 100

class BookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = BookPagination
    # next/previous/count — автоматически в ответе
Преимущество: DRF автоматически добавляет count, next, previous в ответ. Ручная пагинация требует делать это вручную и не совместима с Browsable API.

2. Формат LOGGING (из лекции) → современный Django 3.2+

Из лекции (старый стиль форматтера — %-стиль)

# settings.py — %-стиль форматтера (Python 2 / ранний Django)
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(message)s',
            # style не указан — по умолчанию '%'
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': 'db.log',   # относительный путь — плохая практика
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
        },
    },
}

Современное (Django 3.2+ / Django 5.x)

# settings.py — {}-стиль форматтера + абсолютные пути
import os

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '[{asctime}] {levelname} {name} — {message}',
            'style': '{',    # явно указываем стиль
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': os.path.join(BASE_DIR, 'logs', 'db_logs.log'),  # абсолютный путь!
            'formatter': 'verbose',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': False,  # не дублировать вверх по иерархии
        },
    },
}

3. CursorPagination без кастомного класса → с кастомным

Из лекции (упрощённо — работает только если в модели есть created_at)

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
    'PAGE_SIZE': 5,
}
# Если в модели нет created_at — AttributeError при первом запросе!

Современная практика — всегда кастомный класс для CursorPagination

# pagination.py
from rest_framework.pagination import CursorPagination

class CustomCursorPagination(CursorPagination):
    page_size = 6
    ordering = 'published_date'  # укажите поле, которое реально есть в модели
# settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'first_app.pagination.CustomCursorPagination',
    'PAGE_SIZE': 6,
}
Правило: никогда не используйте 'rest_framework.pagination.CursorPagination' напрямую в DEFAULT_PAGINATION_CLASS без проверки наличия поля created_at во всех моделях. Создайте кастомный класс — это явно и безопасно.

4. disable_existing_loggers: True vs False

Опасный вариант (из старых примеров в интернете)

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,  # ОПАСНО — отключит ВСЕ встроенные логгеры Django!
    ...
}

Правильный вариант

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,  # встроенные логгеры Django продолжат работу
    ...
}
Почему важно: при True отключаются django.request, django.security, django.server — вы перестанёте видеть критические ошибки и предупреждения безопасности. Почти всегда нужно False.