在Django REST Framework中将一组对象写为单个字典

我创建了一组模型,允许用户为Django项目中的某些对象定义自定义字段.这允许用户存储与其特定用例相关的数据,而无需修改数据库模式.例如,项目包含一个内置的站点模型,该模型具有名称,地址等.如果用户还希望存储(例如,每个站点的指定联系点),则可以为此模型创建自定义字段.

首先,用户创建自定义字段并将其分配给他们想要拥有该字段的模型.这由CustomField对象表示. (这里提供了该模型的简化版本.对于任何感兴趣的人来说,完整的源代码是available here.)

class CustomField(models.Model):
    obj_type = models.ManyToManyField(ContentType, related_name='custom_fields', verbose_name='Object(s)')
    type = models.PositiveSmallIntegerField(choices=CUSTOMFIELD_TYPE_CHOICES, default=CF_TYPE_TEXT)
    name = models.CharField(max_length=50, unique=True)
    label = models.CharField(max_length=50, blank=True)

第二个模型包含每个对象的自定义字段数据:

class CustomFieldValue(models.Model):
    field = models.ForeignKey('CustomField', related_name='values')
    obj_type = models.ForeignKey(ContentType, related_name='+', on_delete=models.PROTECT)
    obj_id = models.PositiveIntegerField()
    obj = GenericForeignKey('obj_type', 'obj_id')
    serialized_value = models.CharField(max_length=255)

因此,在我们的示例中,我们将为Site模型创建一个名为point_of_contact的CustomField,为每个具有POC的Site创建一个CustomFieldValue实例.

我创建了一个序列化程序,将API中的自定义字段表示为单个子对象.例如,网站可能显示为:

{
    "id": 42,
    "name": "My Site",
    "slug": "site-1",
    "physical_address": "123 Fake St",
    ...
    "custom_fields": {
        "point_of_contact": "Homer Simpson",
        "decommissioned": false
    }
}

序列化器的简化版本如下(full version):

class CustomFieldSerializer(serializers.Serializer):
    """
    Extends a ModelSerializer to render any CustomFields and their values associated with an object.
    """
    custom_fields = serializers.SerializerMethodField()

    def get_custom_fields(self, obj):

        # Gather all CustomFields applicable to this object
        fields = {cf.name: None for cf in self.context['view'].custom_fields}

        # Attach any defined CustomFieldValues to their respective CustomFields
        for cfv in obj.custom_field_values.all():
            fields[cfv.field.name] = cfv.value

        return fields

custom_fields上下文由自定义APIView(full version)提供:

class CustomFieldModelAPIView(object):
    """
    Include the applicable set of CustomField in the view context.
    """
    def __init__(self):
        super(CustomFieldModelAPIView, self).__init__()
        self.content_type = ContentType.objects.get_for_model(self.queryset.model)
        self.custom_fields = self.content_type.custom_fields.all()

这非常适合读取操作,但我坚持如何通过API支持创建和修改自定义字段.问题的关键似乎是序列化程序期望处理包含单个对象而不是一组对象的一组字段.

如何扩展此序列化程序以支持将多个CustomFieldValues作为单个对象进行写入访问?任何指针都非常感谢.

最佳答案 用这个:

class MySerializer(serializers.ModelSerializer):
    custom_fields = serializer.SerializerMethodField(read_only=True)
    custom_fields_write = serializer.DictField(write_only=True)

    class Meta:
        model = Site
        fields = '__all__'

    def create(self, validated_data):
        custom_fields_data = validated_data.pop('custom_fields_write')
        site = super(MySerializer, self).create(validated_data)
        for key, val in custom_fields_data.items():
            cf = CustomField.objects.get(name=key)
            CustomFieldValue.objects.create(field=cf, obj=site, serialized_value=val)
        return site

您可以使用类似的更新方法.

点赞