プロジェクトの Web API を開発するためにDjango Rest Frameworkを使用しています。私のプロジェクトのように、ネストされた API のエンドポイントを次のように構築する必要があります。
/users/ - to get all users
/users/<user_pk> - to get details of a particular user
/users/<user_pk>/mails/ - to get all mails sent by a user
/users/<user_pk>/mails/<pk> - to get details of a mail sent by a user
したがって、これらのネストされたリソースの作成と保守を容易にするために、drf-nested-routersを使用しています。
すべてのエンドポイントの出力に、ネストされた各リソースの詳細と次のような他の詳細を取得するためのハイパーリンクが必要です。
[
{
"url" : "http://localhost:8000/users/1",
"first_name" : "Name1",
"last_name": "Lastname"
"email" : "name1@xyz.com",
"mails": [
{
"url": "http://localhost:8000/users/1/mails/1",
"extra_data": "This is a extra data",
"mail":{
"url": "http://localhost:8000/mails/3"
"to" : "abc@xyz.com",
"from": "name1@xyz.com",
"subject": "This is a subject text",
"message": "This is a message text"
}
},
{
..........
}
..........
]
}
.........
]
HyperlinkedModelSerializer
これを行うには、 DRF docs に従って継承によってシリアライザーを作成します。これにより、url
シリアライゼーション中に応答してフィールドが自動的に追加されます。
ただし、デフォルトでは、DRF シリアライザーは、上記のようにネストされたリソースの URL の生成をサポートしていません。または、複数のルックアップ フィールドを言うことができます。この状況に対処するために、彼らはカスタム ハイパーリンク フィールドを作成することを推奨しました。
このドキュメントに従い、ネストされたリソースの URL 生成を処理するためのカスタム コードを記述します。私のコードスニペットは次のとおりです。
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
# User model
class User(models.AbstractUser):
mails = models.ManyToManyField('Mail', through='UserMail',
through_fields=('user', 'mail'))
# Mail model
class Mail(models.Model):
to = models.EmailField()
from = models.EmailField()
subject = models.CharField()
message = models.CharField()
# User Mail model
class UserMail(models.Model):
user = models.ForeignKey('User')
mail = models.ForeignKey('Mail')
extra_data = models.CharField()
serializers.py
from rest_framework import serializers
from .models import User, Mail, UserMail
from .serializers_fields import UserMailHyperlink
# Mail Serializer
class MailSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Mail
fields = ('url', 'to', 'from', 'subject', 'message' )
# User Mail Serializer
class UserMailSerializer(serializers.HyperlinkedModelSerializer):
url = UserMailHyperlink()
mail = MailSerializer()
class Meta:
model = UserMail
fields = ('url', 'extra_data', 'mail')
# User Serializer
class UserSerializer(serializers.HyperlinkedModelSerializer):
mails = UserMailSerializer(source='usermail_set', many=True)
class Meta:
model = User
fields = ('url', 'first_name', 'last_name', 'email', 'mails')
serializers_fields.py
from rest_framework import serializers
from rest_framework.reverse import reverse
from .models import UserMail
class UserMailHyperlink(serializers.HyperlinkedRelatedField):
view_name = 'user-mail-detail'
queryset = UserMail.objects.all()
def get_url(self, obj, view_name, request, format):
url_kwargs = {
'user_pk' : obj.user.pk,
'pk' : obj.pk
}
return reverse(view_name, kwargs=url_kwargs, request=request,
format=format)
def get_object(self, view_name, view_args, view_kwargs):
lookup_kwargs = {
'user_pk': view_kwargs['user_pk'],
'pk': view_kwargs['pk']
}
return self.get_queryset().get(**lookup_kwargs)
ビュー.py
from rest_framework import viewsets
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from .models import User, UserMail
from .serializers import UserSerializer, MailSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserMailViewSet(viewsets.ViewSet):
queryset = UserMail.objects.all()
serializer_class = UserMailSerializer
def list(self, request, user_pk=None):
mails = self.queryset.filter(user=user_pk)
serializer = self.serializer_class(mails, many=True,
context={'request': request}
)
return Response(serializer.data)
def retrieve(self, request, pk=None, user_pk=None):
queryset = self.queryset.filter(pk=pk, user=user_pk)
mail = get_object_or_404(queryset, pk=pk)
serializer = self.serializer_class(mail,
context={'request': request}
)
return Response(serializer.data)
urls.py
from rest_framework.routers import DefaultRouter
from rest_framework_nested import routers
from django.conf.urls import include, url
import views
router = DefaultRouter()
router.register(r'users', views.UserViewSet, base_name='user')
user_router = routers.NestedSimpleRouter(router, r'users',
lookup='user'
)
user_router.register(r'mails', views.UserMailViewSet,
base_name='user-mail'
)
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^', include(user_router.urls)),
]
/users/
さて、プロジェクトを実行してAPIエンドポイントにpingを実行したときにこれを行った後、次のエラーが発生しました:
AttributeError : 'UserMail' オブジェクトに属性 'url' がありません
UserMailSerializer
このシリアライザーの属性としてフィールドを追加したため、このエラーが発生した理由を理解できませんでした。そのため、モデルの属性としてフィールドをurl
取る理由をシリアル化する必要があります。この問題から逃れるために私を助けてください。url
UserMail
PS:モデルのリファクタリングを提案しないでください。として、ここで私は自分のプロジェクトの本当のアイデアをuser
&シングで偽装しmail
ました。したがって、これをテストケースとして、解決策を提案してください。