Search code examples
django-rest-frameworkdjango-serializer

How can I add a field to a model serializer that has a reverse relationship with another model


I have two models. Fiction and Review model. They are the following:

class Fiction(models.Model):
    """
    Model that encopasses a Movie, TV Series, book or similar
    """
    MOVIE       = 1
    TV_SERIES   = 2
    BOOK        = 3
    PODCAST     = 4

    TYPE = (
        (MOVIE, 'Movie'),
        (TV_SERIES, 'TV-Series'),
        (BOOK, 'Book'),
        (PODCAST, 'Podcast')
    )


    title = models.CharField(max_length=50)
    description = models.CharField(max_length=200)
    active = models.BooleanField(default=True)
    created = models.DateTimeField(auto_now_add=True)
    platform = models.ForeignKey(
        StreamPlatform, 
        on_delete=models.SET_NULL, 
        related_name='fictions',
        null = True
        )
    type = models.PositiveSmallIntegerField(
        choices = TYPE,
        default = MOVIE
    )

    def __str__(self):
        return self.title

and

class Review(models.Model):
    """
    model for fiction reviews from users
    """
    rating = models.PositiveSmallIntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
    fiction = models.ForeignKey(Fiction, on_delete=models.CASCADE, related_name="reviews")
    description = models.CharField(max_length=200, null = True, blank =True)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return str(self.rating) + " | " + str(self.fiction)

    class Meta:
        ordering = ['-created']

and also two serializers for fiction

class FictionSerializer(serializers.ModelSerializer):
    """
    serializer for Movie model
    """

    class Meta:
        model = Fiction
        fields = "__all__"

and for review

class ReviewSerializer(serializers.ModelSerializer):

    class Meta:
        model = Review
        fields = ['rating', 'fiction', 'description']

I want to be able to display the rating of the review inside the fiction serializers. I tried something like:

rating = serializers.ReadOnlyField(source='reviews.rating')

but it didnt work. Anyone has an idea?


Solution

  • Since you added reviews as a related name, you can use that. Here is a working example for you. (I've created a small project for this, so this definitely works)

    class ReviewRatingSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Review
            fields = ('rating', )
    
    
    
    class FictionSerializer(serializers.ModelSerializer):
        """
        serializer for Movie model
        """
        reviews = ReviewRatingSerializer(many=True)
    
        class Meta:
            model = Fiction
            fields = "__all__"
    

    This might cause lots of database queries if you want to return lots of Fiction items at once. To fix that, you should use prefetch_related in your views.py

    Here is a simple example for a list view.

    class GetFictionMovies(ListAPIView):
        pagination_class = None
        serializer_class = FictionSerializer
    
        def get_queryset(self):
            queryset = Fiction.objects.all().prefetch_related('reviews')
            return queryset
    

    Output will be similar to this.

    [
        {
            "id": 1,
            "reviews": [
                {
                    "rating": 3
                },
                {
                    "rating": 4
                }
            ],
            "title": "Starwars",
            "description": "asdasd",
            "active": true,
            "created": "2021-06-27T16:28:55.521748Z",
            "type": 1
        },
        {
            "id": 2,
            "reviews": [
                {
                    "rating": 5
                },
                {
                    "rating": 2
                }
            ],
            "title": "LOTR",
            "description": "asdasd",
            "active": true,
            "created": "2021-06-27T16:29:03.227639Z",
            "type": 1
        },
        {
            "id": 3,
            "reviews": [
                {
                    "rating": 4
                },
                {
                    "rating": 3
                }
            ],
            "title": "GODFATHER",
            "description": "asdasd",
            "active": true,
            "created": "2021-06-27T16:34:45.171444Z",
            "type": 1
        }
    ]
    

    My advice for you is to always check for number of queries made to the db and try to avoid duplicate calls to the db.