ManyToManyFields with a Through Model
Published on mayo 10th, 2019
Many times I found myself, many collegues and some other people struggling when working with ManyToManyFields and trying to serialize the relation with a through Model.
I think the official documentation of the amazing django-rest-framework does not provide a clear example on how to do it (may be it's a too specific use case). So I decided to leave here, as a reminder to myself and to anybody else, a small clarifitacion on how it works.
Example, given the following models:
class Artist(models.Model):
name = models.CharField(max_length=180)
class Portfolio(models.Model):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
name = models.CharField(max_length=180)
artists = models.ManyToManyField(Artist, through='Calification')
class Calification(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name='califications')
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
rate = models.IntegerField(default=0)
added_at = models.DateTimeField(auto_now_add=True)
We could serialize them as follows:
class ArtistSerializer(serializers.ModelSerializer):
class Meta:
model = Artist
fields = ('name', )
class CalificationSerializer(serializers.ModelSerializer):
artist = ArtistSerializer()
class Meta:
model = Calification
fields = ('artist', 'rate', 'added_at')
class PortfolioSerializer(serializers.ModelSerializer):
artists = CalificationSerializer(many=True, source='califications')
class Meta:
model = Portfolio
fields = ('id', 'name', 'user', 'artists', )
And we would obtain a result like the following:
[
{
"id": 1,
"name": "Portfolio 1",
"user": 1,
"califications": [
{
"artist": {
"name": "Gamma Ray"
},
"rate": 3,
"added_at": "2019-04-13T02:51:38.663626+02:00"
},
{
"artist": {
"name": "Frank Zappa"
},
"rate": 2,
"added_at": "2019-04-13T02:51:38.665095+02:00"
},
{
"artist": {
"name": "Pink Floyd"
},
"rate": 1,
"added_at": "2019-04-13T02:51:38.666302+02:00"
}
]
}
]
It's important to remember that it's easier if we set a related_name
to the foreign_key we want to follow from the main model.
If we don't set it we would have to set the attribute source
of the nested serializer CalificationSerializer
as
artists = CalificationSerializer(many=True, source='calification_set.all')
By default, relational fields that target a ManyToManyField with a through model specified are set to read-only.
If you explicitly specify a relational field pointing to a ManyToManyField with a through model, be sure to set read_only to True.