Search code examples
pythondjangodjango-modelsdjango-rest-frameworkmany-to-many

Django Rest Framework nested manytomany serializer


I have to models as below:

class PositionModel(BaseModel):
    """
    User Position/Designation Model
    """
    name = models.CharField(max_length=255)

    class Meta:
        ordering = ["created_at"]

    def __str__(self):
        return f"{self.name}"


class SuperiorModel(BaseModel):
    """
    Super model for position
    """
    position = models.OneToOneField(PositionModel, on_delete=models.CASCADE, related_name='position_superior')
    superior = models.ManyToManyField(PositionModel, blank=True)

    def __str__(self):
        return f'{self.position.name}'

Signals:

@receiver(post_save, sender=PositionModel)
def create_position_instances(sender, instance, created, **kwargs):
    if created:
        SuperiorModel.objects.create(
            position=instance
        )


@receiver(post_save, sender=PositionModel)
def save_position_instances(sender, instance, **kwargs):
    instance.position_superior.save()

What I want here is to create position with superior model.

My expected payload for post is this:

{
  "name": "test position",
  "superior": [1,2] # <-- Could be empty array if there is no superior
}

So that when I creates a position the superior value could be created in the SuperiorModel.

Also in the get method I want to get the data same as the payload.


Solution

  • I did this in the following way:

    class SuperiorSerializer(serializers.ModelSerializer):
        """
        Superior Serializer
        """
    
        class Meta:
            model = models.SuperiorModel
            fields = '__all__'
    
    
    class PositionSerializer(serializers.ModelSerializer):
        """
        Serializer for Employee Position
        """
    
        position_superior = SuperiorSerializer(read_only=True)
        superior = serializers.ListField(write_only=True)
    
        class Meta:
            model = models.PositionModel
            fields = [
                "id",
                "name",
                "is_active",
                "position_superior",
                "superior",
            ]
            extra_kwargs = {
                "is_active": {"read_only": True},
            }
    
        @staticmethod
        def update_helper(instance, validated_data):
    
            superior = validated_data.pop("superior")
            instance.name = validated_data.get("name", instance.name)
            superiors = models.SuperiorModel.objects.get(
                position=instance
            )
            superiors.superior.set(superior)
            instance.save()
      
            return instance
    
        def create(self, validated_data):
    
            superior = validated_data.pop("superior")
            position = models.PositionModel.objects.create(
                is_active=True, **validated_data
            )
    
            superiors = models.SuperiorModel.objects.get(
                position=position
            )
            superiors.superior.set(superior)
            return position
    
        def update(self, instance, validated_data):
            return self.update_helper(instance, validated_data)
    
    

    I don't know if it is the optimized or recommended way. If it's not, any help would be appreciated.