🔖 Справочник — Урок 31

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

⚡ Быстрый справочник

# Extract* — импорт
from django.db.models.functions import ExtractYear, ExtractMonth, ExtractQuarter

# Прямая фильтрация (lookups)
qs = Model.objects.filter(date_field__year=2023)
qs = Model.objects.filter(date_field__month=1)
qs = Model.objects.filter(date_field__quarter=2)

# Через annotate
qs = Model.objects.annotate(yr=ExtractYear('date_field')).filter(yr=2023)

# query_params в APIView
val = request.query_params.get('key')         # None если нет
val = request.query_params.get('key', 'def')  # дефолт

# Фильтрация через словарь
filters = {}
if author: filters['author'] = author
qs = Model.objects.filter(**filters)

# Сортировка
sort_by = request.query_params.get('sort_by', 'title')
sort_order = request.query_params.get('sort_order', 'asc')
if sort_order == 'desc': sort_by = f'-{sort_by}'
qs = qs.order_by(sort_by)

# Пагинация
results = self.paginate_queryset(qs, request, view=self)
return self.get_paginated_response(serializer.data)

Extract* — полная таблица функций

ФункцияLookup-суффиксДиапазонОписание
ExtractYear__yearнапр. 2023Год из даты/времени
ExtractMonth__month1–12Месяц из даты/времени
ExtractDay__day1–31День из даты/времени
ExtractWeekDay__week_day1–7 (1=вс)День недели
ExtractWeek__week1–53Номер недели (ISO)
ExtractQuarter__quarter1–4Квартал года
ExtractHour__hour0–23Час из времени
ExtractMinute__minute0–59Минута из времени
ExtractSecond__second0–59Секунда из времени

Синтаксис Extract*

Импорт

from django.db.models.functions import (
    ExtractYear, ExtractMonth, ExtractDay,
    ExtractWeekDay, ExtractWeek, ExtractQuarter,
    ExtractHour, ExtractMinute, ExtractSecond
)

Прямые lookups (без импорта функций)

# Поле дата: published_date
Model.objects.filter(published_date__year=2023)
Model.objects.filter(published_date__month=1)
Model.objects.filter(published_date__quarter=2)
Model.objects.filter(published_date__week_day=3)  # вторник
Model.objects.filter(published_date__week=10)
Model.objects.filter(created_at__hour=14)         # DateTimeField

Аннотация + фильтрация

from django.db.models.functions import ExtractYear

qs = Model.objects.annotate(
    yr=ExtractYear('published_date')
).filter(yr=2023)

# Аннотация для агрегации (группировка по году)
from django.db.models import Count
qs = Model.objects.annotate(
    yr=ExtractYear('published_date')
).values('yr').annotate(total=Count('id')).order_by('yr')

request.query_params — синтаксис

# Получить одно значение
val = request.query_params.get('key')           # None если нет
val = request.query_params.get('key', 'default') # значение по умолчанию

# Получить список значений (для параметра ?tags=a&tags=b)
vals = request.query_params.getlist('tags')     # ['a', 'b']

# Проверить наличие параметра
if 'filter' in request.query_params:
    ...

# Получить все параметры как словарь
all_params = dict(request.query_params)  # {'key': ['val'], ...}

Паттерн: динамическая фильтрация

class BookListView(APIView):
    def get(self, request):
        filters = {}

        author = request.query_params.get('author')
        pub_year = request.query_params.get('pub_year')
        genre = request.query_params.get('genre')

        if author:
            filters['author__icontains'] = author  # регистронезависимо

        if pub_year and pub_year.isdigit():
            filters['published_date__year'] = int(pub_year)

        if genre:
            filters['genre__name'] = genre

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

Паттерн: сортировка

ALLOWED_SORT_FIELDS = {'title', 'price', 'published_date', 'author'}

class BookListView(APIView):
    def get(self, request):
        sort_by = request.query_params.get('sort_by', 'title')
        sort_order = request.query_params.get('sort_order', 'asc')

        # Валидация поля сортировки
        if sort_by not in ALLOWED_SORT_FIELDS:
            sort_by = 'title'

        if sort_order == 'desc':
            sort_by = f'-{sort_by}'

        books = Book.objects.all().order_by(sort_by)
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)

Паттерн: пагинация в APIView

from rest_framework.pagination import PageNumberPagination

class BookListView(APIView, PageNumberPagination):
    page_size = 10          # дефолтный размер страницы
    page_size_query_param = 'page_size'  # имя параметра в URL
    max_page_size = 100     # максимальный размер

    def get(self, request):
        books = Book.objects.all()
        results = self.paginate_queryset(books, request, view=self)
        serializer = BookSerializer(results, many=True)
        return self.get_paginated_response(serializer.data)
Атрибуты PageNumberPagination:
  • page_size — размер страницы по умолчанию
  • page_size_query_param — имя URL-параметра для page_size
  • page_query_param — имя параметра для номера страницы (по умолч. 'page')
  • max_page_size — максимально допустимый размер страницы

Объединённый паттерн: фильтрация + сортировка + пагинация

class BookListView(APIView, PageNumberPagination):
    page_size = 10

    def get(self, request):
        filters = {}
        author = request.query_params.get('author')
        if author:
            filters['author__icontains'] = author

        sort_by = request.query_params.get('sort_by', 'title')
        sort_order = request.query_params.get('sort_order', 'asc')
        if sort_order == 'desc':
            sort_by = f'-{sort_by}'

        books = Book.objects.filter(**filters).order_by(sort_by)
        results = self.paginate_queryset(books, request, view=self)
        serializer = BookSerializer(results, many=True)
        return self.get_paginated_response(serializer.data)