【Django】 Pass the ModelSerializer an API-authenticated user instance
It was a note because it was slightly jammed.
environment
- Python 3.7.4
- Django 3.0.3
- django-rest-framework 3.11.0
About ModelSerializer
In django-rest-framework (DRF), conversion between Json and Model is performed via an object called ModelSerializer. When registering by editing the value received by the API, returning a record that does not exist in the table, processing data and making it Json, a method is often added to Serializer.
In DRF, when token authentication is performed, the user instance is stored in the request object. When you pass a user instance to Serializer, the value of the relation destination can be automatically entered, so there are many usage scenes.
model
The premise Visitor model is as follows.
import uuid
from django.db import models
from api.models import AppUser
from web.models import AdminUser
class Visitor(models.Model):
class Meta:
db_table = "visitor"
verbose_name_plural = '訪問者'
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
parent_user = models.ForeignKey(AdminUser,
on_delete=models.CASCADE,
related_name='visitor')
name = models.CharField(max_length=50)
email = models.EmailField(max_length=255, null=True, blank=True)
company = models.CharField(max_length=50, blank=True, null=True)
memo = models.TextField(blank=True, null=True, max_length=300)
visit_count = models.PositiveIntegerField(default=0)
created_by = models.ForeignKey(AppUser,
on_delete=models.SET_NULL,
null=True, blank=True,
related_name='visitor')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
serializer
Serializer generates fields tied to the model. To change the value arbitrarily, override it in the SerializerMethodField()
.
The field defined in the SerializerMethodField()
calls a method called get_
and stores the return value.
get_
methods have instance
in their arguments.
Therefore, I thought that it would be passed here when API authentication is performed, but here it seems that the instance itself created at the time of Create is passed.
(By using this, it is also possible to automatically create the data of the relation destination.) )
from rest_framework.serializers import ModelSerializer, SerializerMethodField
class VisitorCreateSerializer(ModelSerializer):
created_by = SerializerMethodField()
parent_user = SerializerMethodField()
class Meta:
model = Visitor
read_only_fields = (
'pk',
'created_at',
'created_by',
)
fields = (
'pk',
'parent_user',
'created_at',
'name',
'email',
'company',
'memo',
'updated_at',
)
def get_created_by(self, instance):
"""送信したユーザーがcreated_byに格納される"""
return str(instance.pk)
def get_parent_user(self, instance):
"""parent_userを自動的に格納する"""
return str(instance.parent_user.pk)
ViewSet
In the View, there is a part that instantiates a Serializer, so you can pass user to instance here. This time I wanted to separate serializer only create, so I overridden the create method and specified Serializer.
# DRF
from rest_framework import status, viewsets
from rest_framework.permissions import IsAuthenticated
from .serializers import VisitorSerializer, VisitorCreateSerializer
from .models import Visitor
from utils.auth.authentication import APIAuthentication
from rest_framework.response import Response
class VisitorViewSet(viewsets.ModelViewSet):
serializer_class = VisitorSerializer # create以外に使用するserializer
queryset = Visitor.objects.all()
authentication_classes = (APIAuthentication,)
permission_classes = (IsAuthenticated,)
def get_queryset(self):
# API認証した場合,userインスタンスはrequest.userに格納される。
admin = self.request.user.parent_user
qs = Visitor.objects.filter(parent_user=admin)
return qs
# createメソッドをオーバーライド
def create(self, request, *args, **kwargs):
# Serializerにrequest.userを渡すと、get_<field名>メソッドにinstanceが渡る
serializer = VisitorCreateSerializer(instance=request.user, data=request.data)
# ここから下はそのまま
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
