2017-11-24 39 views
1

thisのような書き込み可能なネストされたシリアライザを作成する方法と、一般的な外部キー(here)をシリアル化する方法の例があります。Django Rest Framework:一般的な外部キーを持つ書き込み可能なネストされたシリアライザ

しかし、私は同時に両方を行う方法、つまり一般的な外部キーフィールドのためのネストされた書き込み可能なシリアライザを作成する方法を見つけることができません。

class RecurringMeetingRelatedField(serializers.RelatedField): 
    def to_representation(self, value): 
     if isinstance(value, DailyMeeting): 
      serializer = DailyMeetingSerializer(value) 
     elif isinstance(value, WeeklyMeeting): 
      serializer = WeeklyMeetingSerializer(value) 
     else: 
      raise Exception('Unexpected type of tagged object') 
     return serializer.data 


class MeetingSerializer(serializers.ModelSerializer): 
    recurring_meeting = RecurringMeetingRelatedField() 

    class Meta: 
     model = Meeting 
     fields = '__all__' 

私は:私は私のserializers.pyにカスタムフィールドを作成した後

class Meeting(models.Model): 
    # More fields above 
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) 
    object_id = models.PositiveIntegerField() 
    recurring_meeting = GenericForeignKey('content_type', 'object_id') 

class DailyMeeting(models.Model): 
    meeting = GenericRelation(Meeting) 
    # more fields 

class WeeklyMeeting(models.Model): 
    meeting = GenericRelation(Meeting) 
    # more fields 

:私のモデルで

はどちらかDailyMeetingまたはWeeklyMeetingようにすることができGenericForeignKeyMeetingモデルがあり次のようなJSONを渡します。

{ 
    "start_time": "2017-11-27T18:50:00", 
    "end_time": "2017-11-27T21:30:00", 
    "subject": "Test now", 
    "moderators": [41], 
    "recurring_meeting":{ 
     "interval":"daily", 
     "repetitions": 10, 
     "weekdays_only": "True" 
     } 
} 

しかし、問題は、私は次のエラーを取得していますということです。

AssertionError: Relational field must provide a queryset argument, override get_queryset , or set read_only= True .

リレーショナルフィールドはread_onlyでなければならないのはなぜ? read_onlyと設定すると、シリアライザのdataには渡されません。

どのような種類のクエリーセットを用意しなければなりませんか?

+0

私はシナリオを確認していなかったが、あなたは、APIからrecurring_meetingを渡すために持っていけない...(そのシリアライザに追加()メソッドを作成する)ので、あなたが '' –

+0

をread_only''' '設定することができます'' read_only''に設定すると、 '' create() ''メソッドに渡される '' validated_data''には何もありません。この場合、recurring_meetingは完全に無視されます。 – Galil

答えて

3

to_internal_valueも実装する必要があります。また、平文のFieldクラスを使用することもできます。

from rest_framework.fields import Field 

class RecurringMeetingRelatedField(Field): 
    def to_representation(self, value): 
     if isinstance(value, DailyMeeting): 
      serializer = DailyMeetingSerializer(value) 
     elif isinstance(value, WeeklyMeeting): 
      serializer = WeeklyMeetingSerializer(value) 
     else: 
      raise Exception('Unexpected type of tagged object') 
     return serializer.data 

    def to_internal_value(self, data): 
     # you need to pass some identity to figure out which serializer to use 
     # supose you'll add 'meeting_type' key to your json 
     meeting_type = data.pop('meeting_type') 

     if meeting_type == 'daily': 
      serializer = DailyMeetingSerializer(data) 
     elif meeting_type == 'weekly': 
      serializer = WeeklyMeetingSerializer(data) 
     else: 
      raise serializers.ValidationError('no meeting_type provided') 

     if serializer.is_valid(): 
      obj = serializer.save() 
     else: 
      raise serializers.ValidationError(serializer.errors) 

     return obj 

検証がうまくいったなら、あなたはRecurringMeetingRelatedFieldは、例外が発生します、他の場合でMeetingSerializer検証済みのデータでオブジェクトを作成しますよ。

+0

すばらしい答え!ありがとう!私は、 '' meeting'オブジェクト全体を更新すると、 '' recurring_meeting''フィールドは更新されませんが、新しい '' recurring_meeting''が代わりに作成されることが分かりました。あなたのソリューションに何かをしているのかどうかは分かりませんが、それがどうにか関係しているかどうかを尋ねるだけです... – Galil

+0

@Galil私は更新と削除のケースについて考えなかった。私は今のところPCを持っていないので、私は解決策をテストすることはできません。しかし、更新呼び出し時に 'recurring_meeting'フィールドをjsonから除外し、' RecurringMeetingSerializer'に 'required = False'を追加するだけでよいので、' to_internal_value'呼び出しをスキップします。 –

0

この場合、MeetingシリアライザでRecurringMeetingRelatedFieldを使用する代わりに、このようなネストされたシリアライザを定義できます。

class RecurringMeetingSerializer(serializers.Serializer): 
    interval = serializers.CharField() 
    repetitions = serializers.IntegerField() 
    weekdays_only = serializers.BooleanField() 

    class Meta: 
     fields = '__all__' 


class MeetingSerializer(serializers.ModelSerializer): 
    recurring_meeting = RecurringMeetingSerializer() 

    class Meta: 
     model = Meeting 
     exclude = ['object_id', 'content_type'] 

    def create(self, validated_data): 
     recurring_meeting = validated_data.pop('recurring_meeting') 

     if recurring_meeting['interval'] == 'daily': 
      instance = DailyMeeting.objects.create(**recurring_meeting) 
      type = ContentType.objects.get_for_model(instance) 
     else: 
      instance = WeeklyMeeting.objects.create(**recurring_meeting) 
      type = ContentType.objects.get_for_model(instance) 

     meeting = Meeting.objects.create(content_type=type, 
             object_id=instance.id) 

     return meeting