Django Rest Framework:具有通用外键的可写嵌套序列化程序
Posted
技术标签:
【中文标题】Django Rest Framework:具有通用外键的可写嵌套序列化程序【英文标题】:Django Rest Framework: Writable nested serializers with Generic Foreign Key 【发布时间】:2018-05-08 17:13:22 【问题描述】:有一些示例如何创建像this 这样的可写嵌套序列化程序,以及如何序列化通用外键 (here)。
但我找不到如何同时执行这两项操作,即如何为通用外键字段创建嵌套的可写序列化程序。
在我的模型中,有一个 Meeting
模型和一个 GenericForeignKey
,它可以是 DailyMeeting
或 WeeklyMeeting
,例如:
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
然后我在serializers.py
中创建了一个自定义字段:
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__'
我传递的 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:关系字段必须提供
queryset
参数,覆盖get_queryset
,或设置 read_only=True
。
为什么关系字段必须是read_only
?如果我将其设置为read_only
,那么它不会在序列化程序中的data
中传递。
我必须提供什么类型的查询集?
【问题讨论】:
我没有检查场景但是你不必从 api 传递 recurring_meeting .. (它添加在序列化程序 create() 方法中)所以你可以设置read_only
如果将其设置为read_only
,则validated_data
中没有任何内容传递给create()
方法。因此,在这种情况下,recurring_meeting 将被完全忽略。
【参考方案1】:
您还需要实现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
如果验证顺利,那么您将在 MeetingSerializer
验证数据中创建对象,否则 RecurringMeetingRelatedField
将引发异常。
【讨论】:
很好的答案!谢谢!我刚刚发现当我更新整个meeting
对象时,嵌套的recurring_meeting
字段没有更新,而是创建了一个新的recurring_meeting
。不确定它是否与您的解决方案有任何关系,只是询问它是否相关......
@Galil 我没有考虑更新和删除的情况。我现在没有电脑,所以我无法测试解决方案。但是您可以尝试在更新调用时从 json 中排除 recurring_meeting
字段并将required=False
添加到RecurringMeetingSerializer
,这样您就可以跳过to_internal_value
调用。【参考方案2】:
在这种情况下,您可以像这样定义一个嵌套的序列化程序,而不是在会议序列化程序中使用 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
【讨论】:
以上是关于Django Rest Framework:具有通用外键的可写嵌套序列化程序的主要内容,如果未能解决你的问题,请参考以下文章
使用 Django Rest Framework 3.2.2 具有现有对象的可写嵌套序列化程序
Django Rest Framework:具有通用外键的可写嵌套序列化程序
Django REST Framework 序列化程序字段必需=false