🐛 Типичные ошибки — Урок 31
⚡ Топ-3 ошибки урока
- None в filter(): добавляем параметр в filters без проверки —
filter(author=None)возвращает пустой queryset - Строка в year-фильтре:
filter(published_date__year="2023")может работать, ноfilter(published_date__year=None)сломает запрос - Сортировка без валидации:
order_by(sort_by)без белого списка — клиент может передать несуществующее поле
Ошибка 1: None в словаре filters
Проблема
# НЕПРАВИЛЬНО — параметр добавляется всегда
class BookListView(APIView):
def get(self, request):
author = request.query_params.get('author')
filters = {'author': author} # author может быть None!
books = Book.objects.filter(**filters)
...
Если параметр не передан, author = None, и filter(author=None) вернёт только книги с author=None или пустой queryset.
Решение
# ПРАВИЛЬНО — добавляем только при наличии значения
author = request.query_params.get('author')
filters = {}
if author:
filters['author'] = author
books = Book.objects.filter(**filters) # без фильтра = все книги
Ошибка 2: Числовые параметры без преобразования типа
Проблема
# НЕПРАВИЛЬНО — год как строка
year = request.query_params.get('year')
filters = {}
if year:
filters['published_date__year'] = year # строка "2023"
# ХУЖЕ — год без проверки isdigit()
if year:
filters['published_date__year'] = int(year) # ValueError если year="abc"
Решение
# ПРАВИЛЬНО
year = request.query_params.get('year')
filters = {}
if year and year.isdigit():
filters['published_date__year'] = int(year)
elif year:
# Неверный формат — можно вернуть ошибку
return Response(
{'error': 'year must be a number'},
status=400
)
Ошибка 3: Сортировка без валидации поля
Проблема
# НЕПРАВИЛЬНО — клиент может передать произвольное поле
sort_by = request.query_params.get('sort_by', 'title')
books = Book.objects.order_by(sort_by)
# ?sort_by=password_hash — раскрывает схему
# ?sort_by=nonexistent — FieldError
Решение
ALLOWED_SORT_FIELDS = {'title', 'price', 'published_date', 'author'}
sort_by = request.query_params.get('sort_by', 'title')
if sort_by.lstrip('-') not in ALLOWED_SORT_FIELDS:
sort_by = 'title' # сброс к дефолту
books = Book.objects.order_by(sort_by)
Ошибка 4: Пагинация без проверки results на None
Проблема
# НЕПРАВИЛЬНО — paginate_queryset может вернуть None
class BookListView(APIView, PageNumberPagination):
page_size = 5
def get(self, request):
books = Book.objects.all()
results = self.paginate_queryset(books, request, view=self)
# Если results = None (нет пагинации) — serializer получит None
serializer = BookDetailSerializer(results, many=True)
return self.get_paginated_response(serializer.data)
paginate_queryset возвращает None, если пагинация не применяется (например, параметр page не передан и page_size не ограничивает). В этом случае нужна ветка для обычного ответа.
Решение
def get(self, request):
books = Book.objects.all()
results = self.paginate_queryset(books, request, view=self)
if results is not None:
serializer = BookDetailSerializer(results, many=True)
return self.get_paginated_response(serializer.data)
# Пагинация не применена — обычный ответ
serializer = BookDetailSerializer(books, many=True)
return Response(serializer.data)
Ошибка 5: Использование request.data вместо request.query_params
Проблема
# НЕПРАВИЛЬНО — request.data для GET-параметров
class BookListView(APIView):
def get(self, request):
author = request.data.get('author') # всегда None для GET!
request.data содержит тело запроса (POST, PUT, PATCH). Для GET-запросов тело обычно пустое. Параметры URL нужно читать через request.query_params.
Решение
# ПРАВИЛЬНО
author = request.query_params.get('author') # из URL ?author=...
Ошибка 6: ExtractWeekDay — неочевидная нумерация
Проблема
# Ожидаем: понедельник = 1
tasks = Task.objects.filter(due_date__week_day=1)
# Получаем: воскресенье!
# ExtractWeekDay использует нумерацию:
# 1 = Воскресенье, 2 = Понедельник, ..., 7 = Суббота
# (ISO: 1 = Понедельник, 7 = Воскресенье)
Решение
# Используйте словарь-маппинг
WEEKDAY_MAP = {
'понедельник': 2, # Django: 2
'вторник': 3,
'среда': 4,
'четверг': 5,
'пятница': 6,
'суббота': 7,
'воскресенье': 1, # Django: 1
}
weekday = request.query_params.get('weekday', '').lower()
weekday_num = WEEKDAY_MAP.get(weekday)
if weekday_num:
tasks = Task.objects.filter(due_date__week_day=weekday_num)
⚠️ Проверить по документации: нумерация WeekDay в Django может зависеть от настройки USE_TZ и базы данных.
Ошибка 7: Забыть .as_view() в urls.py
Проблема
# НЕПРАВИЛЬНО — класс без .as_view()
urlpatterns = [
path('books/', BookListView), # TypeError!
]
Решение
# ПРАВИЛЬНО
urlpatterns = [
path('books/', BookListView.as_view(), name='book-list'),
]