到目前为止,我对Django Rest Framework非常满意,这就是为什么我几乎无法相信代码库中存在如此大的遗漏。希望有人知道如何支持这个:
class PinSerializer(serializers.ModelSerializer):
item = ItemSerializer(read_only=True, source='item')
item = serializers.IntegerSerializer(write_only=True)
class Meta:
model = Pin
目标
The goal here is to read:
{pin: item: {name: 'a', url: 'b'}}
but to write using an id
{pin: item: 10}
另一种方法是使用两个序列化程序,但这看起来像一个非常难看的解决方案: django rest framework model serializers - read nested, write flat
答案 0 :(得分:14)
Django允许您使用item
属性访问Pin上的项目,但实际上将关系存储为item_id
。您可以在序列化程序中使用此策略来解决Python对象不能具有两个具有相同名称的属性(您在代码中遇到的问题)的事实。
执行此操作的最佳方法是使用带有PrimaryKeyRelatedField
参数的source
。这将确保完成正确的验证,在字段验证期间(紧接在序列化程序的"item_id": <id>
调用之前)将"item": <instance>
转换为validate
。这允许您在validate
,create
和update
方法期间操纵完整对象。你的最终代码是:
class PinSerializer(serializers.ModelSerializer):
item = ItemSerializer(read_only=True)
item_id = serializers.PrimaryKeyRelatedField(write_only=True,
source='item',
queryset=Item.objects.all())
class Meta:
model = Pin
fields = ('id', 'item', 'item_id',)
注1:我还删除了读取字段上的source='item'
,因为这是多余的。
注2:我实际上发现Django Rest的设置非常不直观,没有指定Item序列化器的Pin序列化器将item_id返回为"item": <id>
而不是"item_id": <id>
,但是点。
此方法甚至可以与正向和反向“多”关系一起使用。例如,您可以使用item_ids
数组来设置项目中的所有引脚,并使用以下代码:
class ItemSerializer(serializers.ModelSerializer):
pins = PinSerializer(many=True, read_only=True)
pin_ids = serializers.PrimaryKeyRelatedField(many=True,
write_only=True,
source='pins',
queryset=Pin.objects.all())
class Meta:
model = Item
fields = ('id', 'pins', 'pin_ids',)
我之前建议的另一个策略是使用IntegerField
直接设置item_id
。假设您使用OneToOneField或ForeignKey将Pin与项目相关联,则可以将item_id
设置为整数,而不使用item
字段。这会削弱验证并导致违反约束的DB级错误。如果你想跳过验证DB调用,特别需要ID而不是验证/创建/更新代码中的对象,或者需要同时具有相同源的可写字段,这可能会更好,但我不会再推荐一下。完整的一行是:
item_id = serializers.IntegerField(write_only=True)
答案 1 :(得分:11)
如果您使用的是DRF 3.0,则可以实现新的to_internal_value
方法来覆盖item字段,将其更改为PrimaryKeyRelatedField以允许平面写入。 to_internal_value
将未经验证的传入数据作为输入,并应返回将作为serializer.validated_data
提供的已验证数据。请参阅文档:http://www.django-rest-framework.org/api-guide/serializers/#to_internal_valueself-data
所以在你的情况下,它将是:
class ItemSerializer(ModelSerializer):
class Meta:
model = Item
class PinSerializer(ModelSerializer):
item = ItemSerializer()
# override the nested item field to PrimareKeyRelatedField on writes
def to_internal_value(self, data):
self.fields['item'] = serializers.PrimaryKeyRelatedField(queryset=Item.objects.all())
return super(PinSerializer, self).to_internal_value(data)
class Meta:
model = Pin
需要注意的两件事:可浏览的web api仍然会认为写入是嵌套的。我不知道如何解决这个问题,但我只使用网络界面进行调试,这不是什么大问题。此外,在您编写返回的项目后,将使用平面项而不是嵌套项。要解决此问题,您可以添加此代码以强制读取始终使用Item序列化程序。
def to_representation(self, obj):
self.fields['item'] = ItemSerializer()
return super(PinSerializer, self).to_representation(obj)
我从Anton Dmitrievsky的回答中得到了这个想法:DRF: Simple foreign key assignment with nested serializers?
答案 2 :(得分:2)
您可以创建自定义序列化器字段(http://www.django-rest-framework.org/api-guide/fields)
示例来自链接:
class ColourField(serializers.WritableField):
"""
Color objects are serialized into "rgb(#, #, #)" notation.
"""
def to_native(self, obj):
return "rgb(%d, %d, %d)" % (obj.red, obj.green, obj.blue)
def from_native(self, data):
data = data.strip('rgb(').rstrip(')')
red, green, blue = [int(col) for col in data.split(',')]
return Color(red, green, blue)
然后在序列化程序类中使用此字段。
答案 3 :(得分:0)
我创建了一个Field类型,该类型试图解决带有Integer中的ForeignKey的数据保存请求以及读取具有嵌套数据的数据的请求
这是课程:
class NestedRelatedField(serializers.PrimaryKeyRelatedField):
"""
Model identical to PrimaryKeyRelatedField but its
representation will be nested and its input will
be a primary key.
"""
def __init__(self, **kwargs):
self.pk_field = kwargs.pop('pk_field', None)
self.model = kwargs.pop('model', None)
self.serializer_class = kwargs.pop('serializer_class', None)
super().__init__(**kwargs)
def to_representation(self, data):
pk = super(NestedRelatedField, self).to_representation(data)
try:
return self.serializer_class(self.model.objects.get(pk=pk)).data
except self.model.DoesNotExist:
return None
def to_internal_value(self, data):
return serializers.PrimaryKeyRelatedField.to_internal_value(self, data)
因此将被使用:
class PostModelSerializer(serializers.ModelSerializer):
message = NestedRelatedField(
queryset=MessagePrefix.objects.all(),
model=MessagePrefix,
serializer_class=MessagePrefixModelSerializer
)
希望对您有帮助。