How to pass extra context to Django Rest Framework serializers

Viewed 4846

It is my first project using Django rest framework and i'm struggling to get this right. I know that in mainstream Django framework, if i need to add extra contexts to a class-based view, i will do something like this:

class PostDetail(DetailView):
    model = Post

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # Add in a QuerySet of all the books by a certain author John
        context['john_books'] = Book.objects.filter(author=john)
        return context

then, i will be able to access the context 'john_books' in my template. Now i need to do same by passing extra contexts to my PostViewSets. On the detail view, i want to access the list of post authored by that post author in my api endpoint (something like 'Posts from the same author'). I have read about get_serializer_context but still can't figure out how to implement it. This is what i have so far:

class PostViewSets(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

     def get_serializer_context(self):
        context = super(PostViewSets, self).get_serializer_context()
        author = self.get_object.author
        author_posts = self.get_queryset().filter(author=author)
        context.update({'author_posts': author_posts})
        return context

i get this error:

AttributeError at /posts/ 'function' object has no attribute 'author'

My Post Model:

class Post(models.Model):
    title = models.CharField(max_length=100, unique=True)
    body = models.TextField()
    is_featured = models.BooleanField(default=True)
    viewcount = models.IntegerField(default=0)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)

and my PostSerializer class:

class PostSerializer(serializers.ModelSerializer):
    author = UserSerializer()

    class Meta:
        model = Post
        fields = ['id', 'title', 'body', 'author', 'viewcount', 'is_featured', 'created']
2 Answers

You have to use get_object as func, not as property:

class PostViewSets(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

     def get_serializer_context(self):
        context = super(PostViewSets, self).get_serializer_context()
        author = self.get_object().author
        author_posts = self.get_queryset().filter(author=author)
        context.update({'author_posts': author_posts})
        return context

1. The Error Message

AttributeError at /posts/ 'function' object has no attribute 'author'

explicitly explains what and where is the problem:

author = self.get_object.author

I guess you tried to do something like this

author = self.get_object().author

2. A DRF ViewSet

responses with data serialized by corresponding Serializer. So you don't need to change the ViewSet, but update the Serializer with something like:

class PostListSerializer(serializers.ModelSerializer):
    ... some fields ...

    class Meta:
        model = Post
        fields = [ ... some fields ... ]


class PostDetailsSerializer(serializers.ModelSerializer):
    ... some fields ...

    author_posts = PostListSerializer(source="author.post_set", many=True, read_only=True)

    class Meta:
        model = Post
        fields = [ ... some fields ... , 'author_posts']

or with SerializerMethodField

    class PostDetailsSerializer(serializers.ModelSerializer):
    ... some fields ...

    author_posts = serializers.SerializerMethodField()

    def get_author_posts(self, obj):
        return PostListSerializer(instance=obj.post_set.all(), many=True).data

    class Meta:
        model = Post
        fields = [ ... some fields ... , 'author_posts']

I didn't try this exact code, but this is the main idea.

Related