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 previous part yet. I’ll wait. Grab your coffee and this code, and let’s go!!.
Dog House Api
For this API, things are a little less straightforward. The complexity lies in the serializer. Firstly, let’s recap the requirement.
For each Dog, we want to assign one or many DogHouses.
For each DogHouse, We want to assign one or Colors.
The serializer needs to handle the ManyToMany relationship (Colors and DogHouse).
DogHouse Model
class Color(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=100)
class DogHouse(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=100)
dog = models.ForeignKey(Dog, on_delete=models.CASCADE)
colors = models.ManyToManyField(Color, related_name=‘ dog_house_color’, null=True)
DogHouse Serializer
class DogHouseColorSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=True)
class Meta:
model = Color
fields = (‘ id’, )
class DogHouseSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False, read_only=True)
name = serializers.CharField(max_length=100, required=False)
dog = serializers.PrimaryKeyRelatedField(many=False, read_only=False, allow_null=True, required=False, queryset=Dog.objects.all())
#Nested Serializer
colors = DogHouseColorSerializer(many=True, read_only=False, allow_null=True, required=False)
class Meta:
model = DogHouse
fields = ‘ __all__’
def create(self, validated_data):
validated_data.pop(‘ id’, None)
colors = validated_data.pop(‘ colors’, None)
instance = DogHouse.objects.create(**validated_data)
if colors is not None:
list = []
for color in colors:
list.append(color[‘ id’])
query = Color.objects.filter(id__in=list)
instance.colors.set(query)
instance.save()
return instance
def update(self, instance, validated_data):
instance.name = validated_data.get(‘ name’, instance.name)
instance.dog = validated_data.get(‘ dog’, instance.dog)
colors = validated_data.pop(‘ colors’, None)
if colors is not None:
list = []
for color in colors:
list.append(color[‘ id’])
query = Color.objects.filter(id__in=list)
instance.colors.set(query)
instance.save()
return instance
In the case of many-to-many relationship, colors, we need to handle this explicitly. It requires extra steps, but it’s not that complicated.
For the function dog_house_api, the pattern follows 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.