Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django rest framework - request context key error

I have two models (Like and News). I am using django-rest-framework to make a web api out of it.

class Like(models.Model):
    user = models.ForeignKey(User)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

class News(models.Model):
    user = models.ForeignKey(User)
    title = models.CharField(max_length=150)
    ...
    likes = GenericRelation(Like)

A Like object has its own user field to store who liked the News object. Now to check if a specific user exists in any of the likes of a News object, I am getting request.user from a SerializerMethodField.

class NewsSerializer(serializers.HyperlinkedModelSerializer):
    user = UserSerializer()
    likes_count = serializers.IntegerField(source='likes.count', read_only=True)
    user_in_likes = serializers.SerializerMethodField()

    class Meta:
        model = News
        fields = ('user', 'title', 'body', 'article_image', 'pub_date', 'likes_count', 'user_in_likes')

    def get_user_in_likes(self, obj):
        user = self.context['request'].user
        what = obj.likes.filter(user=user).exists()
        return what

When I go the /news/ url, I get the json objects including the user_in_likes to true/false for each news object.

However, I have another serialzer for different model which imports NewsSerializer class and other similar serializers:

class ActivityObjectRelatedField(serializers.RelatedField):
    def to_representation(self, value):
        if isinstance(value, User):
            serializer = UserSerializer(value)
        elif isinstance(value, Job):
            serializer = JobSerializer(value)
        elif isinstance(value, News):
            serializer = NewsSerializer(value)
        elif isinstance(value, Tender):
            serializer = TenderSerializer(value)
        else:
            raise Exception('Unexpected type of tagged object')
        return serializer.data    

class ActivitySerializer(serializers.HyperlinkedModelSerializer):
    actor = ActivityObjectRelatedField(read_only=True)
    target = ActivityObjectRelatedField(read_only=True)

    class Meta:
        model = Activity
        fields = ('url', 'actor', 'verb', 'target', 'pub_date')

Now if I visit /activities/, to get the activities objects I am getting an error:

KeyError at /activities/

'request'

And it points to the line of SerializerMethod of NewsSerialize class where self.context['request'].user is used.

Exception Location: /vagrant/myproject/news/serializers.py in get_user_in_likes, line 25

Again if I visit /news/ url, everything is fine and I get news objects. What am I missing here? Why is request not being recognized in the ActivitiesSerializer class? Please help me solve this problem. Thank you.

like image 495
Aamu Avatar asked Jun 21 '16 16:06

Aamu


2 Answers

You are getting this error because you are not passing request in the context when instantiating the NewsSerializer class or other similar serializers in the to_representation() method.

You are manually instantiating the particular serializer class in to_representation() method. So, after instantiation, that particular serializer does not have access to ActivitySerializer's context thereby leading to the error.

You can pass the ActivitySerializer's context during instantiation of the relevant serializer in the to_representation() method.

class ActivityObjectRelatedField(serializers.RelatedField):
    def to_representation(self, value):
        if isinstance(value, User):
            serializer = UserSerializer(value, context=self.context) # pass context
        elif isinstance(value, Job):
            serializer = JobSerializer(value, context=self.context) # pass context
        elif isinstance(value, News):
            serializer = NewsSerializer(value, context=self.context) # pass context
        elif isinstance(value, Tender):
            serializer = TenderSerializer(value, context=self.context) # pass context
        else:
            raise Exception('Unexpected type of tagged object')
        return serializer.data    
like image 176
Rahul Gupta Avatar answered Oct 08 '22 02:10

Rahul Gupta


It seems like you don't populate the context dictionary of NewsSerializer with your request in the /activities/ view.

You probably use a class based view provided by Django REST Framework which populates this dictionary for you (see the get_serializer_context() method) and passes it to the Serializer instance. That's why it's automatically available to your serializer in your /news/ view.

In your /activities/ view, though, the context is passed to ActivitySerializer and isn't (automatically) propagated further from there. That's why there's no request key in your context dictionary of NewsSerializer. You would need to pass your request object to the NewsSerializer instance. To pass extra context to a Serializer you can add a context parameter containing a dictionary when instantiating (see the DRF documentation).

like image 35
Sören Weber Avatar answered Oct 08 '22 01:10

Sören Weber