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

Mad*_*bat 6 python django serialization json django-rest-framework

我正在尝试使用嵌套的“多对多”关系制作序列化程序。目标是获得一个包含序列化相关对象数组的序列化 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)
Run Code Online (Sandbox Code Playgroud)

传入的 JSON 看起来像这样

{
  "name": "My Pizza",
  "toppings": [
    {"name": "cheese", "inventor": "bob"},
    {"name": "tomatoes", "inventor": "alice"}
  ]
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

编辑 #1:这似乎是 Django 或 DRF 中某个地方的真正错误。DRF 似乎在做正确的事情,它检测到它正在处理多对多字段,并尝试使用自定义字段从数据创建浇头并将它们添加到比萨饼中。因为它只有一个pizza 实例和一个字段名,所以它setattr(pizza, 'toppings', toppings)用来做。Django 似乎在做正确的事情。该__set__定义,似乎弄清楚,它需要使用add()方法的经理。但在此过程中,字段名称 'toppings' 丢失并被默认值替换。这是“小写的相关型号名称”。

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

Mad*_*bat 4

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