現在、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
_
そして_views.py
_からのcreate()
:
_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)は関連オブジェクトを参照します(Subject
およびClass
)primary keys。これらは、デフォルトでは、Djangoで整数キーを自動インクリメントします。他の方法で参照したい場合は、いくつかの異なるオプションがあります。
Class
モデルを検索しているからです。たとえば、create
ビューメソッド(POSTの場合)で関連オブジェクトのルックアップをオーバーライドできますが、update
ビューメソッド(PUTおよびPATCHの場合)でも同様のルックアップを処理する必要があります。オプション1:作成および更新で任意の属性を使用してクラスとサブジェクトを検索:
ネストされたクラスシリアライザーを読み取り専用として設定します。
class ExamSerializer(serializers.ModelSerializer):
subject = SubjectSerializer(read_only=True)
clazz = ClassSerializer(read_only=True)
ビューの作成をオーバーライドして、自由形式の属性の関連クラスを検索します。また、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の設計を利用するための最小のコードを生成したものです。
追加のクラスを実行せずに簡単な方法は、自分でシリアル化することです。
class ExamSerializer(serializers.ModelSerializer):
class Meta:
model = Exam
fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details')
def to_representation(self, instance):
data = super().to_representation(instance)
data['subject'] = SubjectSerializer(
Subject.objects.get(pk=data['subject'])).data
data['clazz'] = ClassSerializer(
Class.objects.get(pk=data['clazz'])).data
return data
ネストされたJSONオブジェクトをDRF(Django Rest Framework)に投稿しようとすると、同じ問題が発生しました。
ネストされたシリアライザーの書き込みを適切にセットアップしたら( writable nested serializers のドキュメントを参照)、 browsable API を使用してデータを投稿/入力することにより、それが機能することをテストできますそこ。それが機能し、JSONオブジェクトをポスト/プットするときにネストされたモデルで「このフィールドは必須です」エラーが引き続き発生する場合は、リクエストのコンテンツタイプを設定します。
この回答 必要な解決策を提供しました。以下に要約します。
$.ajax ({
// Other parameters e.g. url, type
data: JSON.stringify(data),
dataType: "json",
contentType: "application/json; charset=utf-8",
});
Jsオブジェクトを「contentType」と「stringify」に設定する必要がありました。
問題を解決するには、このパッケージを使用できます drf-rw-serializers
必要なのは、2つのシリアライザーを使用することです(1つは読み取り用、もう1つは書き込み用)。
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')
class WriteExamSerializer(serializers.ModelSerializer):
subject = SubjectSerializer()
clazz = ClassSerializer()
class Meta:
model = Exam
fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details')
def create(self, validated_data):
subject = validated_data.pop('subject', None)
# logic to update subject
clazz = validated_data.pop('clazz', None)
# logic to update clazz
return super().create(validated_data)
def update(self, validated_data):
subject = validated_data.pop('subject', None)
# logic to update subject
clazz = validated_data.pop('clazz', None)
# logic to update clazz
return super().update(validated_data)
from drf_rw_serializers import generics
from .models import Exam
from .serializers import WriteExamSerializer, ExamSerializer
class ExamListCreateView(generics.ListCreateAPIView):
queryset = Exam.objects.all()
write_serializer_class = WriteExamSerializer
read_serializer_class = ReadExamSerializer