到目前为止,我对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框架模型序列化器 - 读取嵌套,写入平面
假设您使用OneToOneField或ForeignKey将Pin与您的Item相关联,Django将关系存储为 item_id
,但经常将项目抽象为 item
。您可以利用这一点来解决Python对象不能具有两个具有相同名称的属性(您在代码中遇到的问题)的事实。
只需添加 _id
对于write属性的名称,任何写入都将设置基础关系,而任何读取都将使用抽象对象。您的最终代码将是:
class PinSerializer(serializers.ModelSerializer):
item = ItemSerializer(read_only=True)
item_id = serializers.IntegerField(write_only=True)
class Meta:
model = Pin
注1:我也删除了 source='item'
因为这是多余的和改变的 serializers.IntegerSerializer
至 serializers.IntegerField
,因为我认为这一定是一个错字。
注2:我实际上发现Django Rest的设置非常不直观,没有指定Item序列化器的Pin序列化器返回item_id为 "item": <id>
并不是 "item_id": <id>
,但这不是重点。
假设您使用OneToOneField或ForeignKey将Pin与您的Item相关联,Django将关系存储为 item_id
,但经常将项目抽象为 item
。您可以利用这一点来解决Python对象不能具有两个具有相同名称的属性(您在代码中遇到的问题)的事实。
只需添加 _id
对于write属性的名称,任何写入都将设置基础关系,而任何读取都将使用抽象对象。您的最终代码将是:
class PinSerializer(serializers.ModelSerializer):
item = ItemSerializer(read_only=True)
item_id = serializers.IntegerField(write_only=True)
class Meta:
model = Pin
注1:我也删除了 source='item'
因为这是多余的和改变的 serializers.IntegerSerializer
至 serializers.IntegerField
,因为我认为这一定是一个错字。
注2:我实际上发现Django Rest的设置非常不直观,没有指定Item序列化器的Pin序列化器返回item_id为 "item": <id>
并不是 "item_id": <id>
,但这不是重点。
如果您使用的是DRF 3.0,则可以实施新的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仍然会认为写入是嵌套的。我不知道如何解决这个问题,但我只使用Web界面进行调试,这不是什么大问题。此外,在您编写返回的项目后将具有平面项目而不是嵌套项目。要解决此问题,您可以添加此代码以强制读取始终使用Item序列化程序。
def to_representation(self, obj):
self.fields['item'] = ItemSerializer()
return super(PinSerializer, self).to_representation(obj)
我从Anton Dmitrievsky的答案中得到了这个想法: DRF:使用嵌套序列化程序进行简单的外键分配?
您可以创建自定义序列化器字段(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)
然后在序列化程序类中使用此字段。