💻 Примеры — Урок 31
⚡ Ключевые примеры
# Extract* — прямая фильтрация по дате
books = Book.objects.filter(published_date__year=2023)
books = Book.objects.filter(published_date__month=1)
books = Book.objects.filter(published_date__quarter=2)
# Extract* — через annotate
from django.db.models.functions import ExtractYear
books = Book.objects.annotate(yr=ExtractYear('published_date')).filter(yr=2023)
# APIView + query_params
class BookListView(APIView):
def get(self, request):
author = request.query_params.get('author')
filters = {}
if author:
filters['author'] = author
books = Book.objects.filter(**filters)
return Response(BookSerializer(books, many=True).data)
Пример 1: Базовая модель
Все примеры используют модель Book. Убедитесь, что модель создана и выполнены миграции.
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
price = models.DecimalField(max_digits=8, decimal_places=2, default=0)
published_date = models.DateField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
# serializers.py
from rest_framework import serializers
from .models import Book
class BookDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
Пример 2: Прямая фильтрация через lookups
from myapp.models import Book
# Книги, опубликованные в 2023 году
books_2023 = Book.objects.filter(published_date__year=2023)
for book in books_2023:
print(book.title, book.published_date)
# Книги, опубликованные в январе (любой год)
january_books = Book.objects.filter(published_date__month=1)
for book in january_books:
print(book.title, book.published_date)
# Книги из второго квартала (апрель–июнь)
q2_books = Book.objects.filter(published_date__quarter=2)
for book in q2_books:
print(book.title, book.published_date)
# Комбинирование: январь 2023
january_2023 = Book.objects.filter(
published_date__year=2023,
published_date__month=1
)
print(f"Книги из января 2023: {january_2023.count()}")
Пример 3: Аннотация через Extract*
from django.db.models.functions import ExtractYear, ExtractMonth, ExtractQuarter
from django.db.models import Count
from myapp.models import Book
# Аннотация годом и фильтрация
books_2023 = Book.objects.annotate(
year=ExtractYear('published_date')
).filter(year=2023)
print(f"Книг в 2023: {books_2023.count()}")
# Аннотация месяцем и фильтрация
january_books = Book.objects.annotate(
month=ExtractMonth('published_date')
).filter(month=1)
# Аннотация кварталом и фильтрация
q2_books = Book.objects.annotate(
quarter=ExtractQuarter('published_date')
).filter(quarter=2)
# Группировка по году (статистика)
by_year = Book.objects.annotate(
year=ExtractYear('published_date')
).values('year').annotate(
count=Count('id')
).order_by('year')
for row in by_year:
print(f"{row['year']}: {row['count']} книг")
Пример 4: APIView с фильтрацией через query_params
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializers import BookDetailSerializer
class BookListView(APIView):
def get(self, request):
filters = {}
# Читаем параметры из URL
author = request.query_params.get('author')
published_year = request.query_params.get('pub_year')
# Добавляем в фильтр только если параметр передан
if author:
filters['author'] = author
if published_year:
filters['published_date__year'] = published_year
books = Book.objects.filter(**filters)
serializer = BookDetailSerializer(books, many=True)
return Response(serializer.data)
Примеры URL-запросов
# Все книги
GET http://127.0.0.1:8000/books/
# Книги автора
GET http://127.0.0.1:8000/books/?author=George%20Orwell
# Книги конкретного года
GET http://127.0.0.1:8000/books/?pub_year=2023
# Комбинированный фильтр
GET http://127.0.0.1:8000/books/?author=Tolkien&pub_year=2022
Пример 5: APIView с сортировкой
# views.py
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')
books = Book.objects.all()
if sort_order == 'desc':
sort_by = f'-{sort_by}'
books = books.order_by(sort_by)
serializer = BookDetailSerializer(books, many=True)
return Response(serializer.data)
Примеры URL-запросов
# По цене по убыванию
GET http://127.0.0.1:8000/books/?sort_by=price&sort_order=desc
# По дате публикации по возрастанию
GET http://127.0.0.1:8000/books/?sort_by=published_date&sort_order=asc
# По заголовку (дефолт)
GET http://127.0.0.1:8000/books/
Пример 6: APIView с пагинацией
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
from .models import Book
from .serializers import BookDetailSerializer
class BookListView(APIView, PageNumberPagination):
page_size = 5 # по умолчанию 5 объектов на страницу
def get(self, request):
books = Book.objects.all()
# Получаем page_size из запроса или используем дефолт
page_size = self.get_page_size(request)
self.page_size = page_size
results = self.paginate_queryset(books, request, view=self)
serializer = BookDetailSerializer(results, many=True)
return self.get_paginated_response(serializer.data)
def get_page_size(self, request):
"""Переопределение: поддержка параметра page_size."""
page_size = request.query_params.get('page_size')
if page_size and page_size.isdigit():
return int(page_size)
return self.page_size # дефолт
Примеры URL-запросов
# Первая страница (5 объектов)
GET http://127.0.0.1:8000/books/
# Вторая страница
GET http://127.0.0.1:8000/books/?page=2
# Вторая страница с 3 объектами
GET http://127.0.0.1:8000/books/?page=2&page_size=3
Пример ответа
{
"count": 25,
"next": "http://127.0.0.1:8000/books/?page=3&page_size=3",
"previous": "http://127.0.0.1:8000/books/?page=1&page_size=3",
"results": [
{"id": 4, "title": "Animal Farm", "author": "George Orwell", ...},
{"id": 5, "title": "Brave New World", "author": "Aldous Huxley", ...},
{"id": 6, "title": "Fahrenheit 451", "author": "Ray Bradbury", ...}
]
}
Пример 7: Полный APIView — фильтрация + сортировка + пагинация
# views.py — объединённый пример
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
from .models import Book
from .serializers import BookDetailSerializer
ALLOWED_SORT_FIELDS = {'title', 'price', 'published_date', 'author'}
class BookListView(APIView, PageNumberPagination):
page_size = 10
def get(self, request):
# 1. Фильтрация
filters = {}
author = request.query_params.get('author')
pub_year = request.query_params.get('pub_year')
if author:
filters['author__icontains'] = author
if pub_year and pub_year.isdigit():
filters['published_date__year'] = int(pub_year)
# 2. Сортировка
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}'
# 3. Применяем фильтры и сортировку
books = Book.objects.filter(**filters).order_by(sort_by)
# 4. Пагинация
page_size = request.query_params.get('page_size')
if page_size and page_size.isdigit():
self.page_size = int(page_size)
results = self.paginate_queryset(books, request, view=self)
serializer = BookDetailSerializer(results, many=True)
return self.get_paginated_response(serializer.data)
# urls.py
from django.urls import path
from .views import BookListView
urlpatterns = [
path('books/', BookListView.as_view(), name='book-list'),
]
Пример 8: ДЗ-ориентированный — фильтрация по дню недели
Аналог задания 1 из ДЗ 14: эндпоинт для получения задач по дню недели.
# models.py
from django.db import models
class Task(models.Model):
WEEKDAY_CHOICES = [
(1, 'Понедельник'), (2, 'Вторник'), (3, 'Среда'),
(4, 'Четверг'), (5, 'Пятница'), (6, 'Суббота'), (7, 'Воскресенье'),
]
title = models.CharField(max_length=200)
due_date = models.DateField()
def __str__(self):
return self.title
# views.py
from django.db.models.functions import ExtractWeekDay
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Task
from .serializers import TaskSerializer
WEEKDAY_MAP = {
'понедельник': 2, 'вторник': 3, 'среда': 4,
'четверг': 5, 'пятница': 6, 'суббота': 7, 'воскресенье': 1,
}
class TaskListView(APIView):
def get(self, request):
weekday = request.query_params.get('weekday')
if weekday:
weekday_num = WEEKDAY_MAP.get(weekday.lower())
if weekday_num:
tasks = Task.objects.filter(
due_date__week_day=weekday_num
)
else:
tasks = Task.objects.none()
else:
tasks = Task.objects.all()
serializer = TaskSerializer(tasks, many=True)
return Response(serializer.data)
Примеры запросов
# Все задачи
GET http://127.0.0.1:8000/tasks/
# Задачи на вторник
GET http://127.0.0.1:8000/tasks/?weekday=вторник
# Задачи на пятницу
GET http://127.0.0.1:8000/tasks/?weekday=пятница