序列化 DRF 中的自定义相关字段

Posted

技术标签:

【中文标题】序列化 DRF 中的自定义相关字段【英文标题】:Serializing custom related field in DRF 【发布时间】:2017-02-04 00:51:46 【问题描述】:

我正在尝试制作具有嵌套“多对多”关系的序列化程序。目标是让一个序列化的 JSON 对象包含一个序列化相关对象的数组。模型看起来像这样(名称更改,结构保留)

from django.contrib.auth.models import User

PizzaTopping(models.Model):
    name = models.CharField(max_length=255)
    inventor = models.ForeignKey(User)

Pizza(models.Model):
    name = models.CharField(max_length=255)
    toppings = models.ManyToManyField(PizzaTopping)

传入的 JSON 如下所示


  "name": "My Pizza",
  "toppings": [
    "name": "cheese", "inventor": "bob",
    "name": "tomatoes", "inventor": "alice"
  ]

我当前的序列化程序代码如下所示

class ToppingRelatedField(RelatedField):
    def get_queryset(self):
        return Topping.objects.all()

    def to_representation(self, instance):
        return 'name': instance.name, 'inventor': instance.inventor.username

    def to_internal_value(self, data):
        name = data.get('name', None)
        inventor = data.get('inventor', None)
        try:
            user = User.objects.get(username=inventor)
        except Setting.DoesNotExist:
            raise serializers.ValidationError('bad inventor')
        return Topping(name=name, inventor=user)

class PizzaSerializer(ModelSerializer):
    toppings = ToppingRelatedField(many=True)

    class Meta:
        model = Pizza
        fields = ('name', 'toppings')

似乎因为我为自定义字段定义了 to_internal_value(),它应该自动创建/更新多对多字段。但是当我尝试创建比萨饼时,我得到“无法添加”:字段“pizzatopping”的值为 None”ValueError。看起来像是在内心深处,Django 决定多对多字段应该由模型名称来调用。否则我该如何说服它?

编辑 #1:这似乎是 Django 或 DRF 中某个地方的真正错误。 DRF 似乎在做正确的事情,它检测到它正在处理一个 ManyToMany 字段,并尝试使用自定义字段从数据中创建浇头并将它们添加到比萨饼中。由于它只有一个披萨实例和一个字段名称,因此它使用setattr(pizza, 'toppings', toppings) 来执行此操作。 Django 似乎在做正确的事情。 __set__ 已定义,似乎发现它需要在管理器中使用 add() 方法。但在此过程中,字段名称“toppings”会丢失并被默认替换。这是“小写的相关型号名称”。

编辑#2:我找到了解决方案。一旦我被允许,我将在答案中记录它。 RelatedField 子类中的 to_internal_value() 方法似乎需要返回一个已保存的 Topping 实例,以使 ManyToMany 事物正常工作。现有文档显示相反,此链接 (http://www.django-rest-framework.org/api-guide/fields/#custom-fields) 该示例清楚地返回了一个未保存的实例。

【问题讨论】:

【参考方案1】:

似乎有一个未记录的要求。对于使用自定义 ManyToMany 字段的写入操作,自定义字段类to_internal_value() 方法需要在返回之前保存实例。 DRF 文档省略了这一点,制作自定义字段的示例(http://www.django-rest-framework.org/api-guide/fields/#custom-fields)显示了返回未保存实例的方法。我将更新我向 DRF 团队提出的问题。

【讨论】:

您好,我想实施您的解决方案。 RelatedFields 的定义是什么?我在文档中找不到。您能否提供一个实施示例。谢谢【参考方案2】:

我还尝试将多个字段作为 json 返回,但收到错误 unhashable type: 'dict。最后,我在这里发现了我的方法有什么问题 - https://github.com/encode/django-rest-framework/issues/5104

RelatedFields 通常将相关对象表示为单个值 (例如,蛞蝓、主键、网址等)。如果您想提供一个 嵌套对象表示,那么您应该使用nested serializer。

【讨论】:

以上是关于序列化 DRF 中的自定义相关字段的主要内容,如果未能解决你的问题,请参考以下文章

DRF中的Request

在Java中的自定义异常中序列化字段

CSIC_716_20200221drf--自定义外键字段十大接口

drf框架4-views视图家族操作

Rails 中字段的自定义序列化

DRF--验证