Get current user in Model Serializer
Asked Answered
N

5

67

Is it possible to get the current user in a model serializer? I'd like to do so without having to branch away from generics, as it's an otherwise simple task that must be done.

My model:

class Activity(models.Model):
    number = models.PositiveIntegerField(
        blank=True, null=True, help_text="Activity number. For record keeping only.")
    instructions = models.TextField()
    difficulty = models.ForeignKey(Difficulty)
    categories = models.ManyToManyField(Category)
    boosters = models.ManyToManyField(Booster)

    class Meta():
        verbose_name_plural = "Activities"

My serializer:

class ActivitySerializer(serializers.ModelSerializer):

    class Meta:
        model = Activity

And my view:

class ActivityDetail(generics.RetrieveUpdateDestroyAPIView):

    queryset = Activity.objects.all()
    serializer_class = ActivityDetailSerializer

How can I get the model returned, with an additional field user such that my response looks like this:

{
    "id": 1, 
    "difficulty": 1, 
    "categories": [
        1
    ], 
    "boosters": [
        1
    ],
    "current_user": 1 //Current authenticated user here
}
Nutlet answered 14/1, 2015 at 2:16 Comment(0)
N
93

I found the answer looking through the DRF source code.

class ActivitySerializer(serializers.ModelSerializer):
    
    # Create a custom method field
    current_user = serializers.SerializerMethodField('_user')

    # Use this method for the custom field
    def _user(self, obj):
        request = self.context.get('request', None)
        if request:
            return request.user

    class Meta:
        model = Activity
        # Add our custom method to the fields of the serializer
        fields = ('id','current_user')

The key is the fact that methods defined inside a ModelSerializer have access to their own context, which always includes the request (which contains a user when one is authenticated). Since my permissions are for only authenticated users, there should always be something here.

This can also be done in other built-in djangorestframework serializers.

As Braden Holt pointed out, if your user is still empty (ie _user is returning None), it may be because the serializer was not initialized with the request as part of the context. To fix this, simply add the request context when initializing the serializer:

serializer = ActivitySerializer(
    data=request.data,
    context={
        'request': request
    }
)
Nutlet answered 14/1, 2015 at 2:16 Comment(6)
How do you access that variable? I have set mine up similarly, but in the create() method, I can't seem to access the variable. It's also not in the request.POST.Informer
I used above code but giving following error File "/Users/abc/abc/Django/Work/abc/abc/src/MadhaparGamApps/serializers/newsfeedSerializer.py", line 21, in _user user = self.context['request'].user KeyError: 'request'Mascon
For everyone who's getting the request KeyError: You can solve this is by initializing the serializer from the view with the request object as follows: serializer = serializers.RandomSerializer(data=request.data, context={'request':request}) Source: django-rest-framework.org/api-guide/serializers/…Rebellion
@Braden Holt Thanks dude, you saved a lot of time for meJaqitsch
Do you know where DRF insert the request information in serializer's context in it's source code??Foeticide
@Foeticide github.com/encode/django-rest-framework/blob/… called at github.com/encode/django-rest-framework/blob/…Nutlet
S
68

A context is passed to the serializer in REST framework, which contains the request by default. So you can just use self.context['request'].user inside your serializer.

Sclerosed answered 31/7, 2015 at 11:44 Comment(3)
Do you know where DRF insert the request information in serializer's context in it's source code??Foeticide
This is not true any more, you will need to pass an extra context to the serializer. See django-rest-framework.org/api-guide/serializers/…Classieclassification
@Classieclassification Strange, it still works for me without explicitly passing the context down as of DRF 3.9.4.Burp
J
4

I had a similar problem - I tried to save the model that consist user in, and when I tried to use user = serializers.StringRelatedField(read_only=True, default=serializers.CurrentUserDefault()) like on official documentation - but it throws an error that user is 'null'. Rewrite the default create method and get a user from request helped for me:

class FavoriteApartmentsSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField(read_only=True, default=serializers.CurrentUserDefault())

class Meta:
    model = FavoriteApartments
    exclude = (
        'date_added',
    )

def create(self, validated_data):
    favoriteApartment = FavoriteApartments(
        apartment=validated_data['apartment'],
        user=self.context['request'].user
    )
    favoriteApartment.save()
    return favoriteApartment
Jalisajalisco answered 17/10, 2019 at 10:5 Comment(0)
E
2

Actually, the new way to do this is by using HiddenField - view drf documentation:

class ReportSerializer(serializers.ModelSerializer):
author = serializers.HiddenField(default=serializers.CurrentUserDefault())


class Meta:
    model = Report
    fields = ('id', 'type', 'name', 'post', 'comment', "author")

Make sure to add isAuthenticated to the permissions class when you do this or else it gives errors when saving.

However, if the it is optional, just create your own class as by using the example from the docs.

So simply make the field optional by setting required to false, then create your own class as such:

class CurrentUserDefault:

    requires_context = True

    def __call__(self, serializer_field):
        if serializer_field.context['request'].is_authenticated:
            return None
        return serializer_field.context['request'].user
Edbert answered 1/12, 2023 at 10:2 Comment(0)
U
-2

I modified the request.data:

serializer = SectionSerializer(data=add_profile_data(request.data, request.user))

def add_profile_data(data, user):
    data['user'] = user.profile.id
    return data
Uriiah answered 6/9, 2018 at 15:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.