Django Rest Frameworkで少し問題に直面しています。入れ子になったオブジェクトを含むオブジェクトを投稿しようとしています。
ここに私がありますserializers.py
:
class ClassSerializer(serializers.ModelSerializer):
class Meta:
model = Class
fields = ('number', 'letter')
class SubjectSerializer(serializers.ModelSerializer):
class Meta:
model = Subject
fields = ('title',)
class ExamSerializer(serializers.ModelSerializer):
subject = SubjectSerializer()
clazz = ClassSerializer()
class Meta:
model = Exam
fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details')
depth = 1
def create(self, validated_data):
return Exam.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.__dict__.update(**validated_data)
instance.save()
return instance
そしてcreate()
からviews.py
:
def create(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
私はこの問題についてここでいくつかの投稿を読みましたが、それでもそれで立ち往生しています。私はいくつかの方法でそれを修正しようとしましたが、それはまだ戻ってきてい"This field is required."
ます。
ネストされたシリアル化の問題を扱っています。続行する前に、リンクされたドキュメントをお読みください。
あなたの質問はDRFの問題の複雑な領域に関連しているため、シリアライザとビューセットがどのように機能するかを理解するには、いくつかの説明と議論が必要です。
異なるHTTPメソッドに異なるデータ表現を使用して、同じエンドポイントを介してSubject
とClass
データを表現する問題について説明します。これは、データをネストされた形式で表現したい場合に一般的に問題になるためです。たとえば、ドロップダウンセレクターなどを使用して、ユーザーインターフェイスにクリーンな使用に十分な情報を提供したいと考えています。
デフォルトでは、DjangoとDjango REST Framework(DRF)は、関連するオブジェクト(your Subject
とClass
)を主キーで参照します。デフォルトでは、これらはDjangoの自動インクリメント整数キーです。他の方法でそれらを参照したい場合は、これを上書きする必要があります。いくつかの異なるオプションがあります。
Class
モデルを調べているためです)複合(数字、文字)検索語で構成される複合検索)。create
たとえば、ビューメソッド(POSTの場合)で関連オブジェクトのルックアップをオーバーライドできますが、ビューメソッド(update
PUTおよびPATCHの場合)でも同様のルックアップを処理する必要があります。オプション1:作成および更新で任意の属性を使用してクラスと件名を検索します。
ネストされたクラスシリアライザーを読み取り専用に設定します。
class ExamSerializer(serializers.ModelSerializer):
subject = SubjectSerializer(read_only=True)
clazz = ClassSerializer(read_only=True)
ビューのcreateをオーバーライドして、自由形式の属性で関連クラスを検索します。また、DRFがmixinでこれを実装する方法も確認してください。update
これらを正しく処理するには、メソッドをオーバーライドする必要があります。また、この方法をとる場合はPATCH
、PUT
(更新)に加えて(部分更新)サポートも考慮する必要があります。
def create(self, request):
# Look up objects by arbitrary attributes.
# You can check here if your students are participating
# the classes and have taken the subjects they sign up for.
subject = get_object_or_404(Subject, title=request.data.get('subject'))
clazz = get_object_or_404(
Class,
number=request.data.get('clazz_number')
letter=request.data.get('clazz_letter')
)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(clazz=clazz, subject=subject)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
オプション2:シリアライザを読み取りと書き込みに特化し、主キーを使用します。これは慣用的なアプローチです:
最初に、通常の操作(POST、PUT、PATCH)に使用するデフォルトのModelSerializerを定義します。
class ExamSerializer(serializers.ModelSerializer)
class Meta:
model = Exam
fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details')
次に、必要なフィールドを、データを読み取るためにそれらに与えたい種類の表現で上書きします(GET):
class ExamReadSerializer(ExamSerializer):
subject = SubjectSerializer(read_only=True)
clazz = ClassSerializer(read_only=True)
次に、ViewSetのさまざまな操作に使用するシリアライザを指定します。ここでは、読み取り操作のネストされたSubjectおよびClassデータを返しますが、更新操作には主キーのみを使用します(はるかに簡単です)。
class ExamViewSet(viewsets.ModelViewSet):
queryset = Exam.objects.all()
def get_serializer_class(self):
# Define your HTTP method-to-serializer mapping freely.
# This also works with CoreAPI and Swagger documentation,
# which produces clean and readable API documentation,
# so I have chosen to believe this is the way the
# Django REST Framework author intended things to work:
if self.request.method in ['GET']:
# Since the ReadSerializer does nested lookups
# in multiple tables, only use it when necessary
return ExamReadSerializer
return ExamSerializer
ご覧のとおり、オプション2はDRF(get_serializer_class実装)の上に手書きのコードを3行しか含まないため、それほど複雑ではなくエラーが発生しやすいようです。フレームワークのロジックにオブジェクトの表現と作成および更新を理解させるだけです。
私は他にも多くのアプローチを見てきましたが、これまでのところ、私のために維持するコードが最も少なく、DRFの設計をクリーンな方法で活用しているアプローチです。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加