✅ Решения практикума 8
⚡ Все решения — итог
# Паттерн 1: ModelViewSet (Category, Supplier, Address)
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
router = DefaultRouter()
router.register(r'categories', CategoryViewSet)
# Паттерн 2: Generic Views с get_serializer_class (Product, Customer...)
class ProductListCreateView(ListCreateAPIView):
queryset = Product.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return ProductSerializer
return ProductCreateUpdateSerializer
class ProductDetailUpdateDeleteView(RetrieveUpdateDestroyAPIView):
queryset = Product.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return ProductSerializer
return ProductCreateUpdateSerializer
urlpatterns = [
path('products/', ProductListCreateView.as_view()),
path('products/<int:pk>/', ProductDetailUpdateDeleteView.as_view()),
]
# Паттерн 3: filter_backends
class ProductViewSet(viewsets.ModelViewSet):
filter_backends = [DjangoFilterBackend]
filterset_fields = ['category', 'price']
Задание 1.1 — CategoryViewSet
Разбор: ModelViewSet — самый простой способ реализовать полный CRUD. Достаточно указать queryset и serializer_class — DRF сгенерирует все методы (list, create, retrieve, update, partial_update, destroy).
from rest_framework import viewsets
from .models import Category
from .serializers import CategorySerializer
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
Задание 1.2 — Маршруты для Category
Разбор: DefaultRouter автоматически создаёт все URL для ViewSet. Вызов router.register(r'categories', CategoryViewSet) создаёт маршруты /categories/ и /categories/{pk}/.
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import CategoryViewSet
router = DefaultRouter()
router.register(r'categories', CategoryViewSet)
urlpatterns = [
path('', include(router.urls)),
]
Задание 2.1 — SupplierViewSet
Разбор: Аналогично Category — модель Supplier не имеет сложной логики сериализации, поэтому ModelViewSet с единственным сериализатором идеален.
from rest_framework import viewsets
from .models import Supplier
from .serializers import SupplierSerializer
class SupplierViewSet(viewsets.ModelViewSet):
queryset = Supplier.objects.all()
serializer_class = SupplierSerializer
Задание 2.2 — Маршруты для Supplier
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import SupplierViewSet
router = DefaultRouter()
router.register(r'suppliers', SupplierViewSet)
urlpatterns = [
path('', include(router.urls)),
]
Задание 3.1 — ProductListCreateView
Разбор: Для Product нужны два сериализатора: ProductSerializer при GET возвращает вложенные объекты Category и Supplier; ProductCreateUpdateSerializer при POST принимает числовые FK-ключи. Переопределяем get_serializer_class().
from rest_framework.generics import ListCreateAPIView
from .models import Product
from .serializers import ProductSerializer, ProductCreateUpdateSerializer
class ProductListCreateView(ListCreateAPIView):
queryset = Product.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return ProductSerializer
return ProductCreateUpdateSerializer
Ключевое: ListCreateAPIView обрабатывает только GET (список) и POST (создание). Для GET/деталь/PUT/DELETE нужен отдельный класс (задание 3.2).
Задание 3.2 — ProductDetailUpdateDeleteView
Разбор: RetrieveUpdateDestroyAPIView обрабатывает GET (деталь), PUT, PATCH и DELETE. Та же логика get_serializer_class() — при GET возвращаем полный объект, при изменении принимаем FK.
from rest_framework.generics import RetrieveUpdateDestroyAPIView
from .models import Product
from .serializers import ProductSerializer, ProductCreateUpdateSerializer
class ProductDetailUpdateDeleteView(RetrieveUpdateDestroyAPIView):
queryset = Product.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return ProductSerializer
return ProductCreateUpdateSerializer
Задание 3.3 — Маршруты для Product
Разбор: При использовании Generic Views (не ViewSet) маршруты регистрируются вручную через path(). Обязательно вызываем .as_view().
from django.urls import path
from .views import ProductListCreateView, ProductDetailUpdateDeleteView
urlpatterns = [
path('products/', ProductListCreateView.as_view()),
path('products/<int:pk>/', ProductDetailUpdateDeleteView.as_view()),
]
Задание 4.1 — ProductDetailListCreateView
from rest_framework.generics import ListCreateAPIView
from .models import ProductDetail
from .serializers import ProductDetailSerializer, ProductDetailCreateUpdateSerializer
class ProductDetailListCreateView(ListCreateAPIView):
queryset = ProductDetail.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return ProductDetailSerializer
return ProductDetailCreateUpdateSerializer
Задание 4.2 — ProductDetailDetailUpdateDeleteView
from rest_framework.generics import RetrieveUpdateDestroyAPIView
from .models import ProductDetail
from .serializers import ProductDetailSerializer, ProductDetailCreateUpdateSerializer
class ProductDetailDetailUpdateDeleteView(RetrieveUpdateDestroyAPIView):
queryset = ProductDetail.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return ProductDetailSerializer
return ProductDetailCreateUpdateSerializer
Задание 4.3 — Маршруты для ProductDetail
from django.urls import path
from .views import ProductDetailListCreateView, ProductDetailDetailUpdateDeleteView
urlpatterns = [
path('product-details/', ProductDetailListCreateView.as_view()),
path('product-details/<int:pk>/', ProductDetailDetailUpdateDeleteView.as_view()),
]
Задание 5.1 — AddressViewSet
from rest_framework import viewsets
from .models import Address
from .serializers import AddressSerializer
class AddressViewSet(viewsets.ModelViewSet):
queryset = Address.objects.all()
serializer_class = AddressSerializer
Задание 5.2 — Маршруты для Address
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import AddressViewSet
router = DefaultRouter()
router.register(r'addresses', AddressViewSet)
urlpatterns = [
path('', include(router.urls)),
]
Задание 6.1 — CustomerListCreateView
from rest_framework.generics import ListCreateAPIView
from .models import Customer
from .serializers import CustomerSerializer, CustomerCreateUpdateSerializer
class CustomerListCreateView(ListCreateAPIView):
queryset = Customer.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return CustomerSerializer
return CustomerCreateUpdateSerializer
Задание 6.2 — CustomerDetailUpdateDeleteView
from rest_framework.generics import RetrieveUpdateDestroyAPIView
from .models import Customer
from .serializers import CustomerSerializer, CustomerCreateUpdateSerializer
class CustomerDetailUpdateDeleteView(RetrieveUpdateDestroyAPIView):
queryset = Customer.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return CustomerSerializer
return CustomerCreateUpdateSerializer
Задание 6.3 — Маршруты для Customer
from django.urls import path
from .views import CustomerListCreateView, CustomerDetailUpdateDeleteView
urlpatterns = [
path('customers/', CustomerListCreateView.as_view()),
path('customers/<int:pk>/', CustomerDetailUpdateDeleteView.as_view()),
]
Задание 7.1 — OrderListCreateView
from rest_framework.generics import ListCreateAPIView
from .models import Order
from .serializers import OrderSerializer, OrderCreateUpdateSerializer
class OrderListCreateView(ListCreateAPIView):
queryset = Order.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return OrderSerializer
return OrderCreateUpdateSerializer
Задание 7.2 — OrderDetailUpdateDeleteView
from rest_framework.generics import RetrieveUpdateDestroyAPIView
from .models import Order
from .serializers import OrderSerializer, OrderCreateUpdateSerializer
class OrderDetailUpdateDeleteView(RetrieveUpdateDestroyAPIView):
queryset = Order.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return OrderSerializer
return OrderCreateUpdateSerializer
Задание 7.3 — Маршруты для Order
from django.urls import path
from .views import OrderListCreateView, OrderDetailUpdateDeleteView
urlpatterns = [
path('orders/', OrderListCreateView.as_view()),
path('orders/<int:pk>/', OrderDetailUpdateDeleteView.as_view()),
]
Задание 8.1 — OrderItemListCreateView
from rest_framework.generics import ListCreateAPIView
from .models import OrderItem
from .serializers import OrderItemSerializer, OrderItemCreateUpdateSerializer
class OrderItemListCreateView(ListCreateAPIView):
queryset = OrderItem.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return OrderItemSerializer
return OrderItemCreateUpdateSerializer
Задание 8.2 — OrderItemDetailUpdateDeleteView
from rest_framework.generics import RetrieveUpdateDestroyAPIView
from .models import OrderItem
from .serializers import OrderItemSerializer, OrderItemCreateUpdateSerializer
class OrderItemDetailUpdateDeleteView(RetrieveUpdateDestroyAPIView):
queryset = OrderItem.objects.all()
def get_serializer_class(self):
if self.request.method == 'GET':
return OrderItemSerializer
return OrderItemCreateUpdateSerializer
Задание 8.3 — Маршруты для OrderItem
from django.urls import path
from .views import OrderItemListCreateView, OrderItemDetailUpdateDeleteView
urlpatterns = [
path('order-items/', OrderItemListCreateView.as_view()),
path('order-items/<int:pk>/', OrderItemDetailUpdateDeleteView.as_view()),
]
Задание 9.1 — filter_backends для Product
Разбор: Два шага: 1) добавить 'django_filters' в INSTALLED_APPS; 2) добавить filter_backends и filterset_fields в ViewSet. Теперь можно фильтровать: GET /api/products/?category=2&price=100
# settings.py
INSTALLED_APPS = [
# ...
'django_filters',
]
# views.py
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from .models import Product
from .serializers import ProductSerializer, ProductCreateUpdateSerializer
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['category', 'price']
Задание 9.2 — filter_backends для Customer
Разбор: Аналогично Product, но фильтрация по строковым полям first_name и last_name. Работает точное совпадение: ?first_name=Иван.
# views.py
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from .models import Customer
from .serializers import CustomerSerializer, CustomerCreateUpdateSerializer
class CustomerViewSet(viewsets.ModelViewSet):
queryset = Customer.objects.all()
serializer_class = CustomerSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['first_name', 'last_name']
DjangoFilterBackend использует точное совпадение для строк. Для фильтрации по частичному совпадению (icontains) нужно создать отдельный FilterSet класс с явным указанием lookup_expr='icontains'. ⚠️ Проверить по документации: django-filter FilterSet