Сохранение данных через сериализатор Django REST Framework
Очень часто приходится сталкиваться с проектами в которых DRF Serializer используется только для вывода данных. А для ввода данных и их верификации используются какие то отдельные функции. Что как мне кажется совершенно неправильно. Если в проекте используется DRF Serializer то именно он и должен быть задействован для ввода и для вывода.
С помощью DRF Serializer можно:
- вывести сериализованные данные
- проверить входные данные но не сохранять (например аутентифицировать пользователя)
- обработать входные данные и сохранить их в базе
С выводом данных, вообще то все понятно. В этой статье я просто хочу напомнить как можно сохранять полученные данные. Более подробно естественно описано в документации.
При использовании обычных форм Django существует общий шаблон, в котором мы сохраняем форму с помощью commit = False, а затем передаем некоторые дополнительные данные экземпляру перед сохранением их в базе данных, например:
form = InvoiceForm(request.POST) if form.is_valid(): invoice = form.save(commit=False) invoice.user = request.user invoice.save()
Это очень полезно, потому что мы можем сохранить требуемую информацию, используя только один запрос к базе данных, а также позволяет обрабатывать столбцы, не допускающие значения NULL, которые не были определены в форме.
Чтобы смоделировать этот шаблон с помощью сериализатора Django REST Framework, вы можете сделать что-то вроде этого:
serializer = InvoiceSerializer(data=request.data) if serializer.is_valid(): serializer.save(user=request.user)
Вы также можете передать сразу несколько параметров:
serializer = InvoiceSerializer(data=request.data) if serializer.is_valid(): serializer.save(user=request.user, date=timezone.now(), status='sent')
Пример использования APIView
В этом примере я создал приложение с именем core.
models.py
from django.contrib.auth.models import User from django.db import models class Invoice(models.Model): SENT = 1 PAID = 2 VOID = 3 STATUS_CHOICES = ( (SENT, 'sent'), (PAID, 'paid'), (VOID, 'void'), ) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='invoices') number = models.CharField(max_length=30) date = models.DateTimeField(auto_now_add=True) status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES) amount = models.DecimalField(max_digits=10, decimal_places=2)
serializers.py
from rest_framework import serializers from core.models import Invoice class InvoiceSerializer(serializers.ModelSerializer): class Meta: model = Invoice fields = ('number', 'amount')
views.py
from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView from core.models import Invoice from core.serializers import InvoiceSerializer class InvoiceAPIView(APIView): def post(self, request): serializer = InvoiceSerializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save(user=request.user, status=Invoice.SENT) return Response(status=status.HTTP_201_CREATED)
Пример использования ViewSet
Очень похожий пример с теми же models.py и serializers.py, что и в предыдущем примере.
views.py
from rest_framework.viewsets import ModelViewSet from core.models import Invoice from core.serializers import InvoiceSerializer class InvoiceViewSet(ModelViewSet): queryset = Invoice.objects.all() serializer_class = InvoiceSerializer def perform_create(self, serializer): serializer.save(user=self.request.user, status=Invoice.SENT)
У сериалайзера есть два метода базовых сохранения данных create, update, которые можно использовать для привязки соответствующего функционала:
from rest_framework import serializers from core.models import Invoice class InvoiceSerializer(serializers.ModelSerializer): class Meta: model = Invoice fields = ('number', 'amount') def create(self, validated_data): return Comment(**validated_data) def update(self, instance, validated_data): instance.number = validated_data.get('number', instance.number) instance.date = validated_data.get('date', instance.date) return instance
Так же естественно можно переопределить метод save:
from rest_framework import serializers from core.models import Invoice class InvoiceSerializer(serializers.ModelSerializer): class Meta: model = Invoice fields = ('number', 'amount') ... def save(self): ...
В целом у DRF очень большое количество настроек и возможностей по управлению данными, поэтому всем рекомендую использовать его не только для вывода информации но для сохранения.
respect