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

时间:2016-09-26 15:34:11

标签: python json django serialization 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)

传入的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“的值为”无错误“。它看起来像是在内部的某个地方,Django决定应该通过模型名称调用多对多的字段。我怎么说服它呢?

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

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

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:0)

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

RelatedFields通常将相关对象表示为单个值 (例如,子弹,主键,URL等)。如果您想提供 嵌套对象表示形式,那么您应该使用nested serializer