Writing to a collection of objects as a single dictionary in the Django REST Framework

I have created a set of models that allow users to define custom fields for specific objects in a Django project. This allows users to store data related to their particular use case without changing the database schema. For example, a project includes an embedded model Sitethat has a name, address, etc. The user can create a custom field for this model if they also wanted to save, for example, a named contact point for each site.

First, the user creates a custom field and assigns it to the model (s) in which they want to have this field. This is represented by an object CustomField. (Here is a simplified version of the model. A complete source is available here for anyone interested.)

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)

The second model contains custom field data for each object:

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)

So, in our example, we will create a CustomField with a name point_of_contactfor the site model and an instance CustomFieldValuefor each site with POC.

I created a serializer to represent custom fields in the API as one child. For example, a site may appear as:

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

The following is a simplified version of the serializer ( 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

The context is custom_fieldsprovided by custom 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 ​​ ? .

+4
1

:

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

.

+1

Source: https://habr.com/ru/post/1675539/


All Articles