0

I am try to create a simple model that is referred to itself through an intermediate table. Below the code.

class Entity(models.Model):

    .....

    childs = models.ManyToManyField(
       to='Entity',
       symmetrical=False,
       related_name='from_entities',
       verbose_name='Child Entities',
       through='EntityChild',
       through_fields=('from_entity', 'to_entity'))
  

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'entity'
        verbose_name_plural = 'Entities'

The intermediate table below

class EntityChild(models.Model):
    from_entity = models.ForeignKey(
        Entity, on_delete=models.RESTRICT, related_name='from_entity')
    to_entity = models.ForeignKey(
        Entity, on_delete=models.RESTRICT, related_name='to_entity')

    .....,

    type = models.CharField(
        max_length=255, verbose_name='Type')

    class Meta:
        db_table = 'entity_childs'

And the serializeres

class EntityChildSerializer(serializers.ModelSerializer):
    to_entity = serializers.RelatedField(many=False, read_only=True)

    def to_representation(self, value):
        serializer = self.parent.parent.__class__(value, context=self.context)

    class Meta:
        model = EntityChild
        fields = ('to_entity', 'type',....)


class EntitySerializer(serializers.ModelSerializer):

    fields = FieldSerializer(many=True)
    tags = TagSerializer(many=True)

    childs = EntityChildSerializer(source='from_entity', many=True)

    class Meta:
        model = Entity
        fields = ('id', 'childs', 'created_at', 'updated_at')

So at the end my entity child will have a relation ManyToMany with the EntityChild that hold a reference to another entity.

With the code below the serializer will return

{ 
  "id": 1,
  "childs": [
      null
  ],...

}

I tried follow this Nested serializer "Through model" in Django Rest Framework with no luck.

Any help will be appreciated... Thank you all.

LucaT
  • 333
  • 5
  • 13
  • 1
    [Serialize ManyToManyFields with a Through Model in Django REST Framework](https://stackoverflow.com/questions/65493883/serialize-manytomanyfields-with-a-through-model-in-django-rest-framework) – JPG Jan 15 '21 at 08:24
  • Yes...I tried the solution again on the link above doing some modification and now it work. I will put the solution below – LucaT Jan 15 '21 at 09:39

1 Answers1

0

Found the solution following this post Serialize ManyToManyFields with a Through Model in Django REST Framework

with some modification. First the EnityChild serializer:

class EntityChildSerializer(serializers.ModelSerializer):

    class Meta:
        model = EntityChild
        fields = ('...specify the relation extra fields...')

Than the self referenced model Entity (a bit complex) but once you've understand it will be easy to manage.

class EntitySerializer(serializers.ModelSerializer):

    #...here you can define eny extra serializer entity fields...

    childs = serializers.SerializerMethodField() #define a serializer method field

    # This function will return all childs..is necessary put the context label
    def get_childs(self, entity):
        return EntitySerializer(
            entity.childs.all(),
            many=True,
            # should pass this `entity` instance as context variable for filtering
            context={"entity_instance": entity}
        ).data

    class Meta:
        model = Entity
        fields = ('__all__')

        read_only_fields = ('created_at', 'updated_at')

    
    def serialize_entity_child(self, entity_instance):
        # simple method to serialize the through model fields

        # I had to put this extra check.. not sure why but most probably when child is [] I had no entity_instance, and when try to access self.context["entity_instance"] cause a key not found error.
        if "entity_instance" in self.context:

            # here I say..given en entity child give me the EntityChild relation filtered by the the parent and get the first Entitychild.
            entity_child_instance = entity_instance.to_entity.filter(
                from_entity=self.context["entity_instance"]).first()

            if entity_child_instance:
                return EntityChildSerializer(entity_child_instance).data
            return {}
        else:
            return {}

    # Define the child fields representation
    def to_representation(self, instance):
        # import pdb; pdb.set_trace();
        rep = super(EntitySerializer, self).to_representation(instance)
        # Merge Enity and EntityFields in one dict.
        return {**rep, **self.serialize_entity_child(instance)}

So...I hope this can help anyone try to create a self referenced model through an intermediate table in django.

Thank you @JPG

LucaT
  • 333
  • 5
  • 13