🐛 Типичные ошибки — Урок 33

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

⚡ Топ-5 ошибок

  1. Забыть добавить 'django_filters' в INSTALLED_APPS после pip install django-filter
  2. Не вызвать super().get_serializer_context() — теряются request, format, view
  3. Параметр URL в <str:param> не совпадает с lookup_url_kwarg
  4. Не вызвать self.check_object_permissions() в переопределённом get_object()
  5. Использовать filter() вместо get() в get_object() — возвращает QuerySet, а не объект

Ошибки с lookup_field и lookup_url_kwarg

Ошибка 1: параметр URL не совпадает с lookup_url_kwarg

Симптом: AssertionError: Expected view ... to be called with a URL keyword argument named "genre_name". Fix your URL conf...

# НЕПРАВИЛЬНО — параметр URL 'name' не совпадает с lookup_url_kwarg 'genre_name'
# views.py
class GenreDetailView(RetrieveUpdateDestroyAPIView):
    lookup_url_kwarg = 'genre_name'

# urls.py
path('genres/<str:name>/', GenreDetailView.as_view())  # ← name ≠ genre_name
# ПРАВИЛЬНО — параметры совпадают
# urls.py
path('genres/<str:genre_name>/', GenreDetailView.as_view())

Ошибка 2: lookup_field указывает на неуникальное поле

Симптом: MultipleObjectsReturned при запросе.

# НЕПРАВИЛЬНО — поле author не уникально, по нему несколько книг
class BookDetailView(RetrieveAPIView):
    lookup_field = 'author'  # не уникальное поле!
# ПРАВИЛЬНО — использовать только уникальное поле
class BookDetailView(RetrieveAPIView):
    lookup_field = 'isbn'  # уникальное поле
# Или оставить pk (значение по умолчанию)

Ошибки с get_object()

Ошибка 3: использование filter() вместо get()

Симптом: представление возвращает объект QuerySet, а не модель. Сериализатор падает с ошибкой или возвращает неверные данные.

# НЕПРАВИЛЬНО — filter() возвращает QuerySet, не объект
def get_object(self):
    pk = self.kwargs.get('pk')
    book = self.queryset.filter(pk=pk, is_banned=False)  # QuerySet!
    return book
# ПРАВИЛЬНО — get() возвращает единственный объект
def get_object(self):
    pk = self.kwargs.get('pk')
    try:
        book = self.queryset.get(pk=pk, is_banned=False)
    except Book.DoesNotExist:
        raise NotFound(f"Book with id '{pk}' not found or is banned.")
    return book

Ошибка 4: забыть check_object_permissions()

Симптом: object-level permissions не работают — любой пользователь получает доступ к любому объекту.

# НЕПРАВИЛЬНО — object-level permissions игнорируются
def get_object(self):
    pk = self.kwargs.get('pk')
    obj = get_object_or_404(Book, pk=pk)
    return obj  # ← без проверки прав!
# ПРАВИЛЬНО
def get_object(self):
    pk = self.kwargs.get('pk')
    obj = get_object_or_404(Book, pk=pk)
    self.check_object_permissions(self.request, obj)  # явный вызов
    return obj

Ошибки с get_serializer_context()

Ошибка 5: не вызывать super()

Симптом: HyperlinkedRelatedField или HyperlinkedModelSerializer падает с AssertionError: `HyperlinkedRelatedField` requires the request in the serializer context.

# НЕПРАВИЛЬНО — request/format/view потеряны
def get_serializer_context(self):
    return {'include_related': True}  # только наш ключ!
# ПРАВИЛЬНО
def get_serializer_context(self):
    context = super().get_serializer_context()  # {'request', 'format', 'view'}
    context['include_related'] = True
    return context

Ошибка 6: обращение к контексту без проверки наличия ключа

Симптом: KeyError в сериализаторе при прямом вызове (unit-тесты, вложенные сериализаторы).

# НЕБЕЗОПАСНО — ключ может отсутствовать
if self.context['include_related']:  # KeyError если ключа нет
# БЕЗОПАСНО — использовать .get() с дефолтным значением
if self.context.get('include_related', False):

Ошибки с filter_backends

Ошибка 7: не добавить django_filters в INSTALLED_APPS

Симптом: ImproperlyConfigured: 'django_filters' must be in INSTALLED_APPS to use DjangoFilterBackend

# НЕПРАВИЛЬНО
pip install django-filter
# settings.py — забыли добавить!
INSTALLED_APPS = ['django.contrib.admin', ...]  # django_filters отсутствует
# ПРАВИЛЬНО
INSTALLED_APPS = [
    ...
    'django_filters',  # обязательно добавить
]

Ошибка 8: неверное имя параметра в запросе

Симптом: фильтрация не работает, запрос возвращает все данные без фильтрации.

# НЕПРАВИЛЬНО — SearchFilter использует ?search=, не ?q=
GET /books/?q=Gatsby        # ← игнорируется!
GET /books/?filter=Tolstoy  # ← тоже игнорируется
# ПРАВИЛЬНО — стандартные имена параметров
GET /books/?search=Gatsby        # SearchFilter
GET /books/?author=Tolstoy       # DjangoFilterBackend (filterset_fields)
GET /books/?ordering=price       # OrderingFilter

Ошибка 9: забыть минус для сортировки по убыванию

Симптом: данные всегда сортируются по возрастанию.

# Сортировка по возрастанию (по умолчанию)
GET /books/?ordering=price

# Сортировка по убыванию (нужен минус перед именем поля)
GET /books/?ordering=-price   # ← минус важен!