Amistad
Amistad

Reputation: 7410

Update method on Django Rest framework nested serializer for M2M fields

I have three models in my models.py as follows:

class Service(models.Model):
     name = models.CharField(max_length=50, unique=True)
     port = models.PositiveSmallIntegerField()
     protocol = models.CharField(max_length=50)

class ServiceGroup(models.Model):
     name = models.CharField(max_length=50, unique=True)
     services = models.ManyToManyField(Service, through=ServiceToServiceGroup)

class ServiceToServiceGroup(models.Model):
    service = models.ForeignKey(Service)
    service_group = models.ForeignKey(ServiceGroup)

My JSON payload to create a new service group is as follows:

    {
    "name": "test_service_group1",
    "services":["service_1", "service_2"],
    }

Since I have a M2M through table, my strategy to create a new ServiceGroup is to first pop out the services list, create the ServiceGroup with the name and then create the M2M realtionships.

My serializer to create a new ServiceGroup is as follows:

class ServiceGroupCreateUpdateSerializer(serializers.ModelSerializer):
    services = serializers.SlugRelatedField(queryset=Service.objects.all(), 
                                            slug_field='name', many=True)
    class Meta:
        model = ServiceGroup
        fields = ['id', 'name', 'services']

    def create(self, validated_data):
        # Pop the services list out
        services = validated_data.pop('services', None)
        # Create the ServiceGroup with the name 
        service_group = ServiceGroup.objects.create(name=validated_data['name'])
        #Create M2M associations
        for service in services:
            service_id = Service.objects.get(name=service)
            ServiceToServiceGroup.objects.create(service_id=service_id,
                                                 service_group_id= service_group.id)

My question is how do I now write the update method ? My JSON payload remains the same, the only difference being that I pass the instance id in the URL.The pseudo code is as follows:

  1. Pop the services list.
  2. Save the name to the instance id.
  3. Find the existing services linked to the ServiceGroup.
  4. For services common in the existing list and the JSON payload list, do nothing.
  5. For services in the existing list and not in the payload, delete M2M associations.
  6. For services not in the existing list and in the payload, create M2M associations.

This seems like so much work for an update method. Is there an easier way of doing this ?

UPDATE

In [145]: instance = ServiceGroup.objects.get(pk=1)                                                                                                                                                                                                                      

In [146]: instance.services.all()                                                                                                                                                                                                                                        
Out[146]: <QuerySet [<Service: test-create442>]>

In [147]: new_services_list = ['test-create398']                                                                                                                                                                                                                         

In [148]: service_objects = 
Service.objects.filter(name__in=new_services_list).all()                                                                                                                                                                                     

In [149]: service_objects                                                                                                                                                                                                                                                
Out[149]: <QuerySet [<Service: test-create398>]>

In [150]: instance.service_set = service_objects                                                                                                                                                                                                                         

In [151]: instance.save()                                                                                                                                                                                                                                                

In [152]: instance.services.all()                                                                                                                                                                                                                                        
Out[152]: <QuerySet [<Service: test-create442>]>

So, I tried the above and it did not work.

Upvotes: 2

Views: 3026

Answers (1)

aman kumar
aman kumar

Reputation: 3156

you can override the update method

def update(self, instance, validated_data):
    # Pop the services list out
    services = validated_data.pop('services', None)
    instance = super().update(instance, validated_data)
    service_objects = Service.objects.filter(name__in=services).all()
    ServiceToServiceGroup.objects.filter(service_group=instance).delete()
    service_group_obj = []
    for service in service_objects:
        service_group_obj(ServiceToServiceGroup(service=service, service_group=instance))
    ServiceToServiceGroup.objects.bulk_create(service_group_obj)
    return instance

Upvotes: 1

Related Questions