Django Rest Framework

Handling

Many-to(o)-many relationships

Intro

This is the fourth part of the series. Feel free to go back if you haven’t read the pre­vi­ous part yet. I’ll wait. Grab your cof­fee and this code, and let’s go!!.

Dog House Api

For this API, things are a lit­tle less straight­for­ward. The com­plex­ity lies in the seri­al­izer. Firstly, let’s recap the require­ment.

  1. For each Dog, we want to assign one or many Dog­Houses.
    For each Dog­House, We want to assign one or Colors.

The seri­al­izer needs to han­dle the Many­ToMany rela­tion­ship (Colors and Dog­House).

DogHouse Model


class Color(mod­els.Model):
    id = mod­els.BigAuto­Field(pri­mary_key=True)
    name = mod­els.CharField(max_length=100)

class Dog­House(mod­els.Model):
    id = mod­els.BigAuto­Field(pri­mary_key=True)
    name = mod­els.CharField(max_length=100)

    dog = mod­els.For­eignKey(Dog, on_delete=mod­els.CAS­CADE)
    col­ors = mod­els.Many­ToMany­Field(Color, related_name=dog_house_color’, null=True)

DogHouse Serializer

class Dog­House­Color­Seri­al­izer(seri­al­iz­ers.Model­Seri­al­izer):
    id = seri­al­iz­ers.Inte­ger­Field(required=True)

    class Meta:
        model = Color
        fields = (id’, )


class Dog­House­Seri­al­izer(seri­al­iz­ers.Model­Seri­al­izer):
    id = seri­al­iz­ers.Inte­ger­Field(required=False, read_only=True)
    name = seri­al­iz­ers.CharField(max_length=100, required=False)
    dog = seri­al­iz­ers.Pri­ma­ryKey­Related­Field(many=False, read_only=False, allow_null=True, required=False, query­set=Dog.objects.all())

    #Nested Seri­al­izer
    col­ors = Dog­House­Color­Seri­al­izer(many=True, read_only=False, allow_null=True, required=False)
    class Meta:
        model = Dog­House
        fields =__all__’

    def cre­ate(self, val­i­dated_data):

        val­i­dated_data.pop(id’, None)
        col­ors = val­i­dated_data.pop(col­ors’, None)
        instance = Dog­House.objects.cre­ate(**val­i­dated_data)

        if col­ors is not None:
            list =  []
            for color in col­ors:
                list.append(color[id’])

            query = Color.objects.fil­ter(id__in=list)
            instance.col­ors.set(query)

        instance.save()
        return instance

    def update(self, instance, val­i­dated_data):

        instance.name = val­i­dated_data.get(name’, instance.name)
        instance.dog = val­i­dated_data.get(dog’, instance.dog)

        col­ors = val­i­dated_data.pop(col­ors’, None)

        if col­ors is not None:
            list = []
            for color in col­ors:
                list.append(color[id’])

            query = Color.objects.fil­ter(id__in=list)
            instance.col­ors.set(query)
        instance.save()
        return instance

In the case of many-to-many rela­tion­ship, col­ors, we need to han­dle this explic­itly. It requires extra steps, but it’s not that com­pli­cated.

For the func­tion dog_house_api, the pat­tern fol­lows from dog_api. So, I’ll omit the code.

Outro

After you read the code, don’t despair if things don’t click right away. As always, see you next time.