📖 Теория: конспект DRF-блока (Уроки 26–31)
⚡ Краткий конспект
- aggregate() — возвращает словарь вычисленных значений для всего QuerySet.
- annotate() — добавляет вычисленные поля к каждому объекту QuerySet.
- order_by(field) — ASC;
order_by('-field')— DESC; срезы:[:5]. - Subquery/OuterRef — подзапросы в ORM; ExpressionWrapper — оборачивание выражений с явным типом.
- DRF — pip install djangorestframework + 'rest_framework' в INSTALLED_APPS.
- ModelSerializer — автоматически создаёт поля из модели; fields/__all__/exclude.
- is_valid() — валидирует данные;
validated_data— безопасные данные после валидации. - @api_view — декоратор для FBV; APIView — базовый класс для CBV.
- Generic views — ListAPIView, CreateAPIView, RetrieveUpdateDestroyAPIView.
- ModelViewSet — все CRUD-методы; Router — автоматическая маршрутизация.
- query_params —
request.query_params.get('key', default)для фильтрации/сортировки.
1. Агрегация данных в Django ORM (Урок 26)
Агрегация позволяет выполнять вычисления над набором данных — суммировать, считать, находить среднее, минимум, максимум.
Основные функции агрегации
| Функция | Назначение |
|---|---|
Count('id') | Подсчёт количества записей |
Sum('price') | Сумма значений поля |
Avg('price') | Среднее значение |
Min('price') | Минимальное значение |
Max('price') | Максимальное значение |
aggregate() — вычисление по всему QuerySet
Возвращает словарь с результатами. Не возвращает отдельные объекты.
from django.db.models import Avg, Count, Sum, Min, Max
result = Book.objects.aggregate(
total_books=Count('id'),
average_price=Avg('price'),
total_price=Sum('price'),
min_price=Min('price'),
max_price=Max('price'),
)
# {'total_books': 42, 'average_price': 15.5, ...}
annotate() — вычисляемые поля к каждому объекту
Добавляет вычисленное поле к каждой записи QuerySet.
from django.db.models import Count
# Количество книг каждого автора
author_stats = Book.objects.values('author').annotate(
book_count=Count('id')
)
for entry in author_stats:
print(entry['author'], entry['book_count'])
order_by() и срезы QuerySet
# Сортировка по возрастанию
books = Book.objects.order_by('title')
# Сортировка по убыванию
books = Book.objects.order_by('-published_date')
# Несколько полей: сначала автор ASC, затем дата DESC
books = Book.objects.order_by('author', '-published_date')
# Срезы — ограничение числа записей
first_5 = Book.objects.all()[:5]
next_5 = Book.objects.all()[5:10]
top_5_by_price = Book.objects.order_by('price')[:5]
Подзапросы: Subquery и OuterRef
from django.db.models import OuterRef, Subquery, Min
# Аннотировать каждую книгу минимальной ценой книги того же автора
subquery = Book.objects.filter(
author=OuterRef('author')
).values('author').annotate(
min_price=Min('price')
).values('min_price')
books = Book.objects.annotate(min_author_price=Subquery(subquery))
ExpressionWrapper — оборачивание выражений
from django.db.models import F, ExpressionWrapper, fields
books = Book.objects.annotate(
discount_pct=ExpressionWrapper(
(1 - F('discounted_price') / F('price')) * 100,
output_field=fields.FloatField()
)
)
2. Django REST Framework — введение (Урок 27)
DRF — набор инструментов для создания RESTful API поверх Django. Ключевые компоненты: модели, сериализаторы, представления (views), маршрутизация.
Установка и подключение
# Установка
pip install djangorestframework
# settings.py
INSTALLED_APPS = [
...
'rest_framework',
]
Основные концепции REST
| Концепция | Описание |
|---|---|
| Ресурсы | Объекты данных, доступные по URL |
| HTTP-методы | GET, POST, PUT, PATCH, DELETE |
| Статус-коды | 200 OK, 201 Created, 400 Bad Request, 404 Not Found, 204 No Content |
| Сериализация | Преобразование объектов в JSON и обратно |
3. Сериализаторы (Уроки 27–28)
serializers.Serializer — ручное определение
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=100)
author = serializers.CharField(max_length=50)
published_date = serializers.DateField()
serializers.ModelSerializer — автоматически из модели
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__' # все поля
# fields = ['title', 'author', 'price'] # выборочно
# exclude = ['created_at'] # исключить поле
Валидация: is_valid и validated_data
data = {'title': 'Django for Beginners', 'author': 'W. Vincent', 'published_date': '2021-05-21'}
serializer = BookSerializer(data=data)
if serializer.is_valid():
validated = serializer.validated_data # безопасные данные
serializer.save() # вызывает create() или update()
else:
print(serializer.errors) # {'author': [...]}
# Вариант с автоматическим исключением при ошибке
serializer.is_valid(raise_exception=True)
Переопределение полей
class BookSerializer(serializers.ModelSerializer):
created_at = serializers.DateTimeField(read_only=True)
discounted_price = serializers.DecimalField(
max_digits=10, decimal_places=2, write_only=True, required=False
)
class Meta:
model = Book
fields = '__all__'
read_only_fields = ['id', 'created_at']
Переопределение create и update
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['title', 'author', 'published_date', 'price']
def create(self, validated_data):
from django.utils import timezone
validated_data['created_at'] = timezone.now()
return super().create(validated_data)
def update(self, instance, validated_data):
if 'title' in validated_data:
validated_data['title'] = validated_data['title'].strip().title()
return super().update(instance, validated_data)
Кастомная валидация
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
def validate_price(self, value):
if value < 0:
raise serializers.ValidationError("Цена не может быть отрицательной.")
return value
def validate(self, data):
if data.get('discounted_price') and data['discounted_price'] >= data['price']:
raise serializers.ValidationError("Скидочная цена должна быть ниже обычной.")
return data
4. Представления (Views) DRF (Уроки 27–29)
Function-Based View с @api_view
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Book
from .serializers import BookSerializer
@api_view(['GET', 'POST'])
def book_list_create(request):
if request.method == 'GET':
books = Book.objects.all()
serializer = BookSerializer(books, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
elif request.method == 'POST':
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Class-Based View: APIView
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class BookDetailView(APIView):
def get(self, request, pk):
try:
book = Book.objects.get(pk=pk)
except Book.DoesNotExist:
return Response({'error': 'Not found'}, status=status.HTTP_404_NOT_FOUND)
serializer = BookSerializer(book)
return Response(serializer.data)
def put(self, request, pk):
book = Book.objects.get(pk=pk)
serializer = BookSerializer(book, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
book = Book.objects.get(pk=pk)
book.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
5. Generic views и ModelViewSet (Урок 29)
Generic views — готовые классы
| Класс | HTTP-методы | Назначение |
|---|---|---|
ListAPIView | GET | Список объектов |
CreateAPIView | POST | Создание объекта |
RetrieveAPIView | GET | Один объект по pk |
UpdateAPIView | PUT/PATCH | Обновление объекта |
DestroyAPIView | DELETE | Удаление объекта |
ListCreateAPIView | GET, POST | Список + создание |
RetrieveUpdateDestroyAPIView | GET, PUT, PATCH, DELETE | Один объект: чтение + обновление + удаление |
from rest_framework import generics
from .models import Book
from .serializers import BookSerializer
class BookListCreateView(generics.ListCreateAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
class BookDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
ModelViewSet — все CRUD за один класс
from rest_framework import viewsets
from .models import Book
from .serializers import BookSerializer
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
Router — автоматическая маршрутизация
# urls.py
from rest_framework.routers import DefaultRouter
from .views import BookViewSet
router = DefaultRouter()
router.register(r'books', BookViewSet, basename='book')
urlpatterns = router.urls
# Генерирует: GET/POST /books/, GET/PUT/PATCH/DELETE /books/{pk}/
6. query_params — фильтрация и сортировка (Урок 31)
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer
class BookFilterView(APIView):
def get(self, request):
queryset = Book.objects.all()
# Фильтрация по автору
author = request.query_params.get('author')
if author:
queryset = queryset.filter(author__icontains=author)
# Фильтрация по минимальной цене
min_price = request.query_params.get('min_price')
if min_price:
queryset = queryset.filter(price__gte=min_price)
# Сортировка
ordering = request.query_params.get('ordering', 'title')
queryset = queryset.order_by(ordering)
# Пагинация
page = int(request.query_params.get('page', 1))
page_size = int(request.query_params.get('page_size', 10))
start = (page - 1) * page_size
end = start + page_size
queryset = queryset[start:end]
serializer = BookSerializer(queryset, many=True)
return Response(serializer.data)