Book
モデルとAuthor
モデルがあるこのケースを考えてみます。
serializers.py
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = models.Author
fields = ('id', 'name')
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer(read_only=True)
class Meta:
model = models.Book
fields = ('id', 'title', 'author')
viewsets.py
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
これは、本のGET
リクエストを送信するとうまくいきます。本の詳細と入れ子になった著者の詳細を含む入れ子になったシリアライザーで出力を取得します。
ただし、本を作成または更新する場合は、POST
/PUT
/PATCH
に、著者のIDだけでなく、ネストされた詳細を送信する必要があります。著者オブジェクト全体ではなく、著者IDを指定して本オブジェクトを作成/更新できるようにしたい。
だから、私のシリアライザがGET
リクエストに対してこのように見える場所
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer(read_only=True)
class Meta:
model = models.Book
fields = ('id', 'title', 'author')
私のシリアライザは、POST
、PUT
、PATCH
リクエストでは次のようになります
class BookSerializer(serializers.ModelSerializer):
author = PrimaryKeyRelatedField(queryset=Author.objects.all())
class Meta:
model = models.Book
fields = ('id', 'title', 'author')
また、要求のタイプごとに2つの完全に別個のシリアライザーを作成したくありません。 author
のBookSerializer
フィールドを変更するだけです。
最後に、この全体を行うためのより良い方法はありますか?
私見、複数のシリアライザーはますます混乱を生むだけです。
むしろ私は以下の解決策を好みます:
これは最もクリーンなアプローチだと思います。
DRF:GETリクエストのすべてのフィールドを許可するが、POSTを1つのフィールドのみに制限する で私の同様の問題と解決策を参照してください
シリアライザのフィールドを動的に変更できるDRFの機能があります http://www.Django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields
私の使用例:GETでslugフィールドを使用して関係のNice repを表示できるようにしますが、POST/PUTでクラシックな主キーの更新に切り替えます。シリアライザを次のように調整します。
class FooSerializer(serializers.ModelSerializer):
bar = serializers.SlugRelatedField(slug_field='baz', queryset=models.Bar.objects.all())
class Meta:
model = models.Foo
fields = '__all__'
def __init__(self, *args, **kwargs):
super(FooSerializer, self).__init__(*args, **kwargs)
try:
if self.context['request'].method in ['POST', 'PUT']:
self.fields['bar'] = serializers.PrimaryKeyRelatedField(queryset=models.Bar.objects.all())
except KeyError:
pass
KeyErrorは、リクエストなしで、場合によってはユニットテストでコードの初期化時にスローされます。
責任を持って楽しんで使用してください。
ViewSet
でget_serializer_class
メソッドを探しています。これにより、使用するシリアライザの要求タイプをオンに切り替えることができます。
from rest_framework import viewsets
class MyModelViewSet(viewsets.ModelViewSet):
model = MyModel
queryset = MyModel.objects.all()
def get_serializer_class(self):
if self.action in ('create', 'update', 'partial_update'):
return MySerializerWithPrimaryKeysForCreatingOrUpdating
else:
return MySerializerWithNestedData
私がこの問題に対処することになった方法は、関連フィールドであるときに別のシリアライザーを用意することでした。
class HumanSerializer(PersonSerializer):
class Meta:
model = Human
fields = PersonSerializer.Meta.fields + (
'firstname',
'middlename',
'lastname',
'sex',
'date_of_birth',
'balance'
)
read_only_fields = ('name',)
class HumanRelatedSerializer(HumanSerializer):
def to_internal_value(self, data):
return self.Meta.model.objects.get(id=data['id'])
class PhoneNumberSerializer(serializers.ModelSerializer):
contact = HumanRelatedSerializer()
class Meta:
model = PhoneNumber
fields = (
'id',
'contact',
'phone',
'extension'
)
あなたはこのようなことをすることができますが、RelatedSerializerのために:
def to_internal_value(self, data):
return self.Meta.model.objects.get(id=data)
したがって、シリアライズする場合は関連オブジェクトをシリアライズし、デシリアライズする場合は関連オブジェクトを取得するために必要なのはidだけです。
私はそれが少し遅れていることを知っていますが、誰かがそれを必要とする場合に備えて。 drfのサードパーティパッケージには、リクエストクエリパラメータを介して動的シリアライズフィールドの設定を許可するものがあります(公式ドキュメントにリストされています: https:// www.Django-rest-framework.org/api-guide/serializers/#third-party-packages )。
IMOの最も完全なものは次のとおりです。
ここで、(1)には(2)よりも多くの機能があります(何をしたいかによっては多すぎる可能性があります)。
(2)を使用すると、次のようなことができます(リポジトリのreadmeから抽出)。
class CountrySerializer(FlexFieldsModelSerializer):
class Meta:
model = Country
fields = ['name', 'population']
class PersonSerializer(FlexFieldsModelSerializer):
country = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Person
fields = ['id', 'name', 'country', 'occupation']
expandable_fields = {
'country': (CountrySerializer, {'source': 'country', 'fields': ['name']})
}
デフォルトの応答:
{
"id" : 13322,
"name" : "John Doe",
"country" : 12,
"occupation" : "Programmer"
}
GET/person/13322?expand = countryを実行すると、応答は次のように変わります:
{
"id" : 13322,
"name" : "John Doe",
"country" : {
"name" : "United States"
},
"occupation" : "Programmer",
}
入れ子の国オブジェクトから人口が省略されていることに注意してください。これは、埋め込まれたCountrySerializerに渡されるときにフィールドが['name']に設定されたためです。
このようにして、IDだけを含むPOSTリクエストを保持し、詳細を含めるためにGET応答を「展開」できます。