✅ Решения — Урок 31

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

⚡ Быстрые ответы

  • Задание 1: 1-D, 2-A, 3-B, 4-C
  • Задание 2: A
  • Задание 3: B (django.db.models.functions)
  • Задание 4: A и C (page и page_size)
  • Задание 5: PageNumberPagination
  • Задание 6: C (request.query_params.get('key'))

Блок 1: Решения — Extract*

Задание 1

Ответ: 1-D, 2-A, 3-B, 4-C

  • ExtractMonth (1) → D. Извлекает месяц из даты
  • ExtractDay (2) → A. Извлекает день из даты
  • ExtractHour (3) → B. Извлекает час из времени
  • ExtractQuarter (4) → C. Извлекает квартал из даты

Задание 2

Ответ: ABook.objects.filter(published_date__month=1)

Встроенный lookup __month не требует явного импорта функций. Вариант B некорректен (нет суффикса). Вариант C создаёт аннотацию, но не фильтрует. Вариант D используется для получения одного объекта.

Задание 3

Ответ: Bfrom django.db.models.functions import ExtractYear

Все функции Extract* находятся в django.db.models.functions. В django.db.models — агрегации (Count, Sum). В rest_framework.filters — фильтры DRF.

Блок 2: Решения — Query Parameters

Задание 4

Ответ: A и Cpage и page_size

В примере из лекции: GET /books/?page=2&page_size=3. Параметр page — номер страницы (из PageNumberPagination), page_size — размер страницы (переопределяется в get_page_size()). Параметры limit и offset используются в LimitOffsetPagination.

Задание 5

Ответ: PageNumberPagination

Из rest_framework.pagination. Используется через множественное наследование в APIView или через атрибут pagination_class в Generic Views.

Задание 6

Ответ: Crequest.query_params.get('key')

В DRF request.query_params — предпочтительный способ доступа к GET-параметрам. request.GET (вариант A) тоже работает, но это Django-стиль. request.data (вариант D) — для тела POST/PUT-запроса.

Блок 3: Решения — Практические задачи

Задание 7

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer

class BookByYearView(APIView):
    def get(self, request):
        year = request.query_params.get('year')

        if year and year.isdigit():
            books = Book.objects.filter(
                published_date__year=int(year)
            )
        else:
            books = Book.objects.all()

        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)

# urls.py
urlpatterns = [
    path('books/', BookByYearView.as_view(), name='book-by-year'),
]

Ключевые моменты:

  • Проверяем, что year передан (не None) и является числом (.isdigit())
  • При отсутствии параметра — возвращаем все книги (не пустой queryset)
  • Преобразуем в int(year) для корректной работы с lookup

Задание 8

Проблема: словарь filters всегда содержит оба ключа, даже если параметры не переданы. Тогда filter(author=None, published_date__year=None) вернёт пустой queryset вместо всех объектов.

Исправленный код:

class BookListView(APIView):
    def get(self, request):
        author = request.query_params.get('author')
        year = request.query_params.get('year')

        filters = {}  # начинаем с пустого словаря

        if author:    # добавляем только если передан
            filters['author'] = author

        if year and year.isdigit():  # проверяем тип
            filters['published_date__year'] = int(year)

        books = Book.objects.filter(**filters)
        return Response(BookSerializer(books, many=True).data)

Правило: никогда не добавляйте параметр в filters без проверки на None (и на корректность типа для числовых полей).