Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create model instance via Serializer without creating models from nested serialziers?

I have model with many links into it:

class Travel(BaseAbstractModel):

    tags = models.ManyToManyField(
        Tag,
        related_name='travels',
        )
    owner = models.ForeignKey(
        'users.TravelUser',
        related_name='travel_owner'
        )
    payment = models.ForeignKey(
        Payment,
        related_name='travels',
        )
    country = models.ForeignKey(
        Country,
        related_name='travels,
        )
    ........

Many of these models have only two fields with unique name and image. I create serializer for each of these models and put them in TravelSerializer

class TravelBaseSerializer(DynamicFieldsModelSerializer):

    owner = UserSerializer(required=False)
    tags = TagSerializer(many=True)
    payment = PaymentSerializer()
    country = CountrySerializer()  

Based on docs I override create() and update.
The problem is, when I sent JSON data, Django create each model from nested serializers. But I want to create only Travel instance. Also I want receive and respond serialized object not only pk field.

UPDATE
I solved this problem, put code in the answer. Now I can receive and respond with Serializer data without creating object. But I think the DRF provides more elegant approach then I do. It is my first project with DRF, maybe I miss something and there's an easier solution.

like image 786
Ivan Semochkin Avatar asked Jul 19 '16 10:07

Ivan Semochkin


2 Answers

I decide override to_internal_value() put it in custom serailizer and inherit all nested serializers from it:

class NestedRelatedSerializer(serializers.ModelSerializer):

    def to_internal_value(self, data):
        try:
           pk = data['pk']
        except (TypeError, KeyError):
        # parse pk from request JSON
            raise serializers.ValidationError({'_error': 'object must provide pk!'})
        return pk

Get all pk from it and save in create and updated methods:

    def update(self, instance, validated_data):
        # If don't get instance from db, m2m field won't update immediately
        # I don't understand why
        instance = Travel.objects.get(pk=instance.pk)
        instance.payment_id = validated_data.get('payment', instance.payment_id)
        instance.country_id = validated_data.get('country', instance.country_id)
        # update m2m links
        instance.tags.clear()
        instance.tags.add(*validated_data.get('tags'))
        instance.save()
        return instance
like image 118
Ivan Semochkin Avatar answered Nov 10 '22 07:11

Ivan Semochkin


I'm not exactly sure I understand what you want to do, but could setting read_only_fields is the Meta class be what you need ?

class TravelBaseSerializer(DynamicFieldsModelSerializer):

    owner = UserSerializer(required=False)
    tags = TagSerializer(many=True)
    payment = PaymentSerializer()
    country = CountrySerializer()  

    class Meta:
        read_only_fields = ('tags',)

See this section in the docs.

like image 32
AntoineWDG Avatar answered Nov 10 '22 08:11

AntoineWDG