Основы DRF: сериализаторы и API-представления - Python с нуля до Junior: Путь к первой работе в IT - Qpel.AI

Основы DRF: сериализаторы и API-представления

Вы уже умеете создавать веб-приложения на Django, которые показывают данные с помощью шаблонов. Но что, если приложению нужно обмениваться данными с другими сервисами, мобильными приложениями или JavaScript-фронтендом? Тут на помощь приходят API (Application Programming Interface), а Django REST Framework (DRF) — мощный инструмент для их создания в Django.

Что такое Django REST Framework (DRF)?

Django REST Framework (DRF) — это набор инструментов для создания веб-API на Django. Он упрощает разработку RESTful API, предлагая готовые решения для:

  • сериализации данных
  • аутентификации
  • авторизации
  • маршрутизации
  • и многого другого

DRF построен на принципах Django. Если вы знакомы с моделями, представлениями и URL-маршрутизацией Django, освоить DRF будет намного проще.

Сериализаторы: переводчики для данных

В основе DRF лежат сериализаторы. Представьте, что у вас есть данные в базе (например, объект модели Django), и их нужно отправить по сети в формате JSON или XML. Или наоборот: вы получили JSON от клиента и хотите сохранить его в базу. Сериализаторы выполняют эту «переводческую» работу:

  • Сериализация: Превращают сложные данные (как экземпляры моделей Django) в простые типы Python, которые легко преобразовать в JSON, XML или другие форматы.
  • Десериализация: Превращают простые типы Python (полученные из JSON/XML) обратно в сложные данные, например, для сохранения в базу.
  • Валидация: Проверяют входящие данные на соответствие правилам (обязательные поля, типы данных, уникальность).

Создаём сериализатор

Давайте создадим сериализатор для нашей модели Product из интернет-магазина. Вот как она выглядит:

# myapp/models.py
from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    in_stock = models.BooleanField(default=True)

    def __str__(self):
        return self.name

Теперь создадим файл serializers.py в приложении myapp и определим сериализатор:

# myapp/serializers.py
from rest_framework import serializers
from .models import Product

class ProductSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True) # Только для чтения, генерируется БД
    name = serializers.CharField(max_length=100)
    description = serializers.CharField(allow_blank=True)
    price = serializers.DecimalField(max_digits=10, decimal_places=2)
    in_stock = serializers.BooleanField(required=False, default=True)

    def create(self, validated_data):
        """
        Создаёт и возвращает новый экземпляр `Product`
        из проверенных данных.
        """
        return Product.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Обновляет и возвращает существующий экземпляр `Product`
        из проверенных данных.
        """
        instance.name = validated_data.get('name', instance.name)
        instance.description = validated_data.get('description', instance.description)
        instance.price = validated_data.get('price', instance.price)
        instance.in_stock = validated_data.get('in_stock', instance.in_stock)
        instance.save()
        return instance

Здесь мы вручную описали каждое поле. Это даёт полный контроль, но для моделей Django есть способ проще — ModelSerializer.

ModelSerializer: быстрый старт

ModelSerializer автоматически создаёт поля сериализатора на основе полей вашей модели Django. Это сильно ускоряет разработку.

# myapp/serializers.py
from rest_framework import serializers
from .models import Product

class ProductModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ['id', 'name', 'description', 'price', 'in_stock'] # Поля для сериализации
        # Или fields = '__all__' для всех полей модели
        # Или exclude = ['some_field'] для исключения полей

Важно: ModelSerializer сам реализует методы create() и update(). Очень удобно!

API-представления (API Views)

Как и в обычном Django, DRF использует представления (views) для обработки HTTP-запросов и ответов. Но в DRF представления адаптированы специально для работы с API.

Функциональные API-представления (@api_view)

Для простых случаев используйте декоратор @api_view из DRF. Он превращает обычную функцию представления Django в API-представление, которое может обрабатывать запросы и возвращать ответы DRF.

# myapp/views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Product
from .serializers import ProductModelSerializer

@api_view(['GET', 'POST'])
def product_list(request):
    """
    Список всех продуктов или создание нового.
    """
    if request.method == 'GET':
        products = Product.objects.all()
        serializer = ProductModelSerializer(products, many=True) # many=True для списка
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = ProductModelSerializer(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)

@api_view(['GET', 'PUT', 'DELETE'])
def product_detail(request, pk):
    """
    Получение, обновление или удаление одного продукта.
    """
    try:
        product = Product.objects.get(pk=pk)
    except Product.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = ProductModelSerializer(product)
        return Response(serializer.data)

    elif request.method == 'PUT':
        serializer = ProductModelSerializer(product, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        product.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Здесь мы используем Response из rest_framework.response для возврата данных в нужном формате (по умолчанию JSON). request.data автоматически разбирает входящие данные (JSON, формы и т.д.).

Классовые API-представления (Class-based Views)

Для более сложных сценариев и лучшей организации кода DRF предлагает мощные классовые представления. Они позволяют использовать наследование и миксины для создания API-логики.

Базовые классы:

  • APIView: Основа для всех представлений DRF. Позволяет обрабатывать HTTP-методы (GET, POST и т.д.) как отдельные методы класса.
  • GenericAPIView: Расширяет APIView и предоставляет общие методы для работы с queryset'ами и сериализаторами. Очень удобно для типичных операций CRUD (Create, Retrieve, Update, Delete).

Давайте перепишем product_list и product_detail с использованием GenericAPIView и миксинов:

# myapp/views.py
from rest_framework import generics
from .models import Product
from .serializers import ProductModelSerializer

class ProductListCreateAPIView(generics.ListCreateAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductModelSerializer

class ProductRetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductModelSerializer

Посмотрите, насколько чище и лаконичнее стал код! Классы ListCreateAPIView и RetrieveUpdateDestroyAPIView (которые наследуются от GenericAPIView и соответствующих миксинов) предоставляют всю логику для списка/создания и получения/обновления/удаления объектов.

Настройка URL-маршрутизации для API

Теперь свяжем наши API-представления с URL-адресами. Это делается так же, как и в обычном Django, через файл urls.py.

# myapp/urls.py
from django.urls import path
from .views import ProductListCreateAPIView, ProductRetrieveUpdateDestroyAPIView

urlpatterns = [
    path('products/', ProductListCreateAPIView.as_view(), name='product-list-create'),
    path('products/<int:pk>/', ProductRetrieveUpdateDestroyAPIView.as_view(), name='product-retrieve-update-destroy'),
]

Не забудьте включить URL-адреса вашего приложения в главный urls.py проекта:

# myproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('myapp.urls')), # Все API-эндпоинты будут доступны по /api/
]

Теперь запустите сервер Django и перейдите по адресу http://127.0.0.1:8000/api/products/. Вы увидите список продуктов (если они есть) или сможете создать новый, отправив POST-запрос. DRF также предоставляет удобный веб-интерфейс для просмотра и тестирования API прямо в браузере.

Практическое задание

  1. Создайте новое Django-приложение (или используйте существующее) и добавьте в него простую модель, например, Book с полями: title (строка), author (строка), publication_year (целое число).
  2. Создайте ModelSerializer для вашей модели Book.
  3. Реализуйте два классовых API-представления (наследуясь от generics.ListCreateAPIView и generics.RetrieveUpdateDestroyAPIView) для модели Book.
  4. Настройте URL-маршрутизацию для этих представлений.
  5. Запустите сервер и проверьте работу API через браузер или инструмент вроде Postman/Insomnia:
    • GET-запрос на /api/books/ (для получения списка книг).
    • POST-запрос на /api/books/ (для создания новой книги).
    • GET-запрос на /api/books/<id>/ (для получения конкретной книги).
    • PUT/PATCH-запрос на /api/books/<id>/ (для обновления книги).
    • DELETE-запрос на /api/books/<id>/ (для удаления книги).

Поздравляю! Вы сделали первые шаги в создании мощных и гибких API с помощью Django REST Framework. Это открывает огромные возможности для интеграции вашего бэкенда с любыми фронтенд-технологиями и другими сервисами.

В следующем разделе мы углубимся в то, как использовать библиотеку requests для отправки HTTP-запросов к внешним API. Это позволит вашим приложениям взаимодействовать с другими сервисами в интернете.