Django Rest Framework - 读取嵌套数据,写入整数

Thi*_*rry 15 python django rest django-rest-framework

到目前为止,我对Django Rest Framework非常满意,这就是为什么我几乎无法相信代码库中存在如此大的遗漏.希望有人知道如何支持这个:

class PinSerializer(serializers.ModelSerializer):
    item = ItemSerializer(read_only=True, source='item')
    item = serializers.IntegerSerializer(write_only=True)

    class Meta:
        model = Pin
Run Code Online (Sandbox Code Playgroud)

有目标

The goal here is to read:
{pin: item: {name: 'a', url: 'b'}}
but to write using an id
{pin: item: 10}
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用两个序列化程序,但这看起来像一个非常难看的解决方案: django rest框架模型序列化程序 - 读取嵌套,写入平面

Jon*_*rds 15

假设您使用OneToOneField或ForeignKey将Pin与您的Item相关联,Django将关系存储为item,但通常将Item抽象为item_id.您可以利用这一点来解决Python对象不能具有两个具有相同名称的属性(您在代码中遇到的问题)的事实.

只需添加PrimaryKeyRelatedFieldwrite属性的名称,任何写入都将设置基础关系,而任何读取都将使用抽象对象.您的最终代码将是:

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',)
Run Code Online (Sandbox Code Playgroud)

注1:我也删除source为是多余的,改"item_id": <id>"item": <instance>,因为我认为这一定是一个错字.

注2:我实际上发现Django Rest的设置非常不直观,没有指定Item序列化器的Pin序列化器返回item_id,validate而不是validate,但这不是重点.


jef*_*fjv 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
Run Code Online (Sandbox Code Playgroud)

有两点需要注意:可浏览的web api仍然会认为写入是嵌套的.我不知道如何解决这个问题,但我只使用Web界面进行调试,这不是什么大问题.此外,在您编写返回的项目后,将使用平面项而不是嵌套项.要解决此问题,您可以添加此代码以强制读取始终使用Item序列化程序.

def to_representation(self, obj):
    self.fields['item'] = ItemSerializer()
    return super(PinSerializer, self).to_representation(obj)
Run Code Online (Sandbox Code Playgroud)

我从Anton Dmitrievsky的答案中得到了这个想法:DRF:使用嵌套序列化器进行简单的外键分配?


Shu*_*huo 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)
Run Code Online (Sandbox Code Playgroud)

然后在序列化器类中使用该字段。

  • 如果以后有人遇到此问题,请注意 DRF 3.0 中的 Field API 已更改。`from_native(self, ob)` 现在是 `to_representation(self, obj)` 并且 `to_native(self, data)` 现在是 `to_internal_value(self, data)` (2认同)