Relations imbriquées dans les sérialiseurs pour les champs OneToOne dans Django Rest Framework
Le Django Rest Framework (DRF) est l'un des frameworks efficacement écrits autour de Django et permet de créer des API REST pour un back-end d'application.
Je l'utilisais dans l'un de mes projets personnels et je suis tombé sur ce défi consistant à « sérialiser un modèle qui fait référence à un autre modèle via le champ OneToOne. »
`J'utilisais le modèle Utilisateur de django.contrib.auth.models
. Je voulais écrire une API pour créer et mettre à jour un objet utilisateur via une seule API qui met également à jour les attributs de mon modèle. La solution consistait à utiliser les Relations imbriquées de DRF dans la sérialisation.
Je suppose que vous avez une bonne connaissance pratique de Python, virtualenv, pip, Django et DRF avant de continuer. Si ce n'est pas le cas, apprenez-en davantage et n'hésitez pas à revenir si jamais vous êtes bloqué sur des relations imbriquées dans la sérialisation.
L'exemple que je considère ici est un modèle Étudiant universitaire, faisant référence au modèle Utilisateur via le champ OneToOne. Mon objectif est une API unique pour créer et obtenir des détails sur l'utilisateur tels que le nom, le nom d'utilisateur et l'adresse e-mail ainsi qu'un attribut d'étudiant tel que le sujet principal.
Voici à quoi ressemble mon models.py
:
from django.db import models
from django.contrib.auth.models import User
class UnivStudent(models.Model):
"""
A class based model for storing the records of a university student
Note: A OneToOne relation is established for each student with User model.
"""
user = models.OneToOneField(User)
subject_major = models.CharField(name="subject_major", max_length=60)
Ensuite, le sérialiseur du modèle ci-dessus détermine les attributs à manipuler. Si vous observez ci-dessous, j'ai 2 classes de sérialiseur, UserSerializer
et StudentSerializer
. C'est notre point d'intérêt.
J'ai déclaré ici un attribut user
qui est un champ de sérialiseur. Cet attribut user
contiendra principalement la référence complète de la classe UserSerializer
. Dans les champs de StudentSerializer
, nous voyons juste « user
» et « subject_major
». Cela nous permet de saisir les attributs de l'étudiant (ou de l'utilisateur) ainsi que le subject_major.
Une entrée utilisateur est créée et référencée par l'entrée étudiant. Nous remplaçons la méthode create de StudentSerializer
pour créer d'abord un objet user
, et l'utilisons pour créer l'objet student
.
Le serializer.py
est le suivant :
from rest_framework import serializers, status
from models import *
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email')
class StudentSerializer(serializers.ModelSerializer):
"""
A student serializer to return the student details
"""
user = UserSerializer(required=True)
class Meta:
model = UnivStudent
fields = ('user', 'subject_major',)
def create(self, validated_data):
"""
Overriding the default create method of the Model serializer.
:param validated_data: data containing all the details of student
:return: returns a successfully created student record
"""
user_data = validated_data.pop('user')
user = UserSerializer.create(UserSerializer(), validated_data=user_data)
student, created = UnivStudent.objects.update_or_create(user=user,
subject_major=validated_data.pop('subject_major'))
return student
Le views.py
devrait être assez simple si vous êtes déjà familier avec les vues basées sur les classes de Django. Nous utiliserons le sérialiseur pour valider et créer les objets modèles :
from serializers import *
from models import *
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class StudentRecordView(APIView):
"""
A class based view for creating and fetching student records
"""
def get(self, format=None):
"""
Get all the student records
:param format: Format of the student records to return to
:return: Returns a list of student records
"""
students = UnivStudent.objects.all()
serializer = StudentSerializer(students, many=True)
return Response(serializer.data)
def post(self, request):
"""
Create a student record
:param format: Format of the student records to return to
:param request: Request object for creating student
:return: Returns a student record
"""
serializer = StudentSerializer(data=request.data)
if serializer.is_valid(raise_exception=ValueError):
serializer.create(validated_data=request.data)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.error_messages,
status=status.HTTP_400_BAD_REQUEST)
J'ai inclus /univstud/
url
pour réaliser les demandes post
et get
pour les étudiants universitaires.
from django.conf.urls import patterns, include, url
from django.contrib import admin
from rest_framework import routers
from rest_framework.urlpatterns import format_suffix_patterns
from OneToOne import views
admin.autodiscover()
router = routers.DefaultRouter()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),
)
urlpatterns += format_suffix_patterns([
# API to map the student record
url(r'^api/univstud/$',
views.StudentRecordView.as_view(),
name='students_list'),
])
L'appel de requête POST
ressemblerait à ceci :
L'appel de requête Get
ressemblerait à ceci :
C'est tout!:)
La relation imbriquée est ainsi activée sur StudentSerializer
pour référencer l'utilisateur
.
Le code complet se trouve dans mon référentiel gitlab.
Les références:
- http://www.django-rest-framework.org/api-guide/relations/