PythonIsGreat
PythonIsGreat

Reputation: 7917

How to get Request.User in Django-Rest-Framework serializer?

I've tried something like this, it does not work.

class PostSerializer(serializers.ModelSerializer):

    class Meta:
        model = Post

    def save(self):
        user = self.context['request.user']
        title = self.validated_data['title']
        article = self.validated_data['article']

I need a way of being able to access request.user from my Serializer class.

Upvotes: 223

Views: 167750

Answers (15)

hadi ahadi
hadi ahadi

Reputation: 146

as others answered, should use self.context['request'].user , but consider it does not work with GenericAPIView , should use ListAPIView or RetrieveAPIView

while GenericAPIView does not send request context to serializer

Upvotes: 0

Gkr
Gkr

Reputation: 69

The best way to get current user inside serializer is like this.

AnySerializer(data={
        'example_id': id
    }, context={'request': request})

This has to be written in views.py And now in Serializer.py part

user = serializers.CharField(default=serializers.CurrentUserDefault())

This "user" must be your field in Model as any relation like foreign key

Upvotes: 2

Benyamin Jafari
Benyamin Jafari

Reputation: 34206

In GET method:

Add context={'user': request.user} in the View class:

class ContentView(generics.ListAPIView):
    def get(self, request, format=None):
        content_list = <Respective-Model>.objects.all()
        serializer = ContentSerializer(content_list, many=True, 
                                       context={'user': request.user})

Get it in the Serializer class method:

class ContentSerializer(serializers.ModelSerializer):
    rate = serializers.SerializerMethodField()

    def get_rate(self, instance):
        user = self.context.get("user") 
        ...  
    ...

In POST method:

Follow other answers (e.g. Max's answer).

Upvotes: 6

ali lotfi
ali lotfi

Reputation: 11

drf srz page

in my project it worked my user field was read only so i needed to get user id in the create method

class CommentSerializer(serializers.ModelSerializer):
    comment_replis = RecursiveField(many=True, read_only=True)
    user = UserSerializer(read_only=True)

    class Meta:
        model = PostComment
        fields = ('_all_')

    def create(self, validated_data):
 


        post = PostComment.objects.create(**validated_data)   
        print(self._dict_['_kwargs']['data']["user"]) # geting #request.data["user"] #  <- mian code
        post.user=User.objects.get(id=self._dict_['_kwargs']['data']["user"])
        return post


in my project i tried this way and it work

Upvotes: 0

You can not access self.context.user directly. First you have to pass the context inside you serializer. For this follow steps bellow:

  1. Some where inside your api view:

     class ApiView(views.APIView):
         def get(self, request):
             items = Item.object.all()
             return Response(
                 ItemSerializer(
                      items, 
                      many=True,
                      context=request  # <- this line (pass the request as context)
                 ).data
             )
    
  2. Then inside your serializer:

     class ItemSerializer(serializers.ModelSerializer):
         current_user = serializers.SerializerMethodField('get_user')
    
         class Meta:
             model = Item
             fields = (
                 'id',
                 'name',
                 'current_user',
             )
    
         def get_user(self, obj):
             request = self.context
             return request.user  # <- here is current your user 
    

Upvotes: 4

Mehran Jalili
Mehran Jalili

Reputation: 723

Use this code in view:

serializer = UploadFilesSerializer(data=request.data, context={'request': request})

then access it with this in serializer:

user = self.context.get("request").user

Upvotes: 18

Sameer
Sameer

Reputation: 121

CurrentUserDefault A default class that can be used to represent the current user. In order to use this, the 'request' must have been provided as part of the context dictionary when instantiating the serializer.

in views.py

serializer = UploadFilesSerializer(data=request.data, context={'request': request})

This is example to pass request

in serializers.py

owner = serializers.HiddenField(
    default=serializers.CurrentUserDefault()
)

Source From Rest Framework

Upvotes: 12

Fernando Tholl
Fernando Tholl

Reputation: 2777

For those who used Django's ORM and added the user as a foreign key, they will need to include the user's entire object, and I was only able to do this in the create method and removing the mandatory field:

class PostSerializer(serializers.ModelSerializer):

def create(self, validated_data):
    
    request = self.context.get("request")
    
    post = Post()
    post.title = validated_data['title']
    post.article = validated_data['article']
    post.user = request.user

    post.save()

    return post

class Meta:
    model = Post
    fields = '__all__'
    extra_kwargs = {'user': {'required': False}}

Upvotes: 11

haccks
haccks

Reputation: 106102

If you are using generic views and you want to inject current user at the point of saving the instance then you can override perform_create or perform_update:

def perform_create(self, serializer):
    serializer.save(user=self.request.user)

user will be added as an attribute to kwargs and you can access it through validated_data in serializer

user = validated_data['user']

Upvotes: 1

IJR
IJR

Reputation: 1338

As Igor mentioned in other answer, you can use CurrentUserDefault. If you do not want to override save method just for this, then use doc:

from rest_framework import serializers

class PostSerializer(serializers.ModelSerializer):
    user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
    class Meta:
        model = Post

Upvotes: 61

Singham
Singham

Reputation: 313

The solution can be simple for this however I tried accessing using self.contenxt['request'].user but not working in the serializer.

If you're using DRF obviously login via token is the only source or maybe others that's debatable.

Moving toward a solution.

Pass the request.user instance while creating serializer.create

views.py

if serializer.is_valid():
            watch = serializer.create(serializer.data, request.user)

serializer.py

 def create(self, validated_data, usr):
    return Watch.objects.create(user=usr, movie=movie_obj, action=validated_data['action'])

Upvotes: 3

Max Malysh
Max Malysh

Reputation: 31615

You can pass request.user when calling .save(...) inside a view:

class EventSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.Event
        exclude = ['user']


class EventView(APIView):

    def post(self, request):
        es = EventSerializer(data=request.data)
        if es.is_valid():
            es.save(user=self.request.user)
            return Response(status=status.HTTP_201_CREATED)
        return Response(data=es.errors, status=status.HTTP_400_BAD_REQUEST)

This is the model:

class Event(models.Model):
    user = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    date = models.DateTimeField(default=timezone.now)
    place = models.CharField(max_length=255)

Upvotes: 7

cgl
cgl

Reputation: 1247

You need a small edit in your serializer:

class PostSerializer(serializers.ModelSerializer):

    class Meta:
        model = Post

    def save(self):
        user = self.context['request'].user
        title = self.validated_data['title']
        article = self.validated_data['article']

Here is an example, using Model mixing viewsets. In create method you can find the proper way of calling the serializer. get_serializer method fills the context dictionary properly. If you need to use a different serializer then defined on the viewset, see the update method on how to initiate the serializer with context dictionary, which also passes the request object to serializer.

class SignupViewSet(mixins.UpdateModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):

    http_method_names = ["put", "post"]
    serializer_class = PostSerializer

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        kwargs['context'] = self.get_serializer_context()
        serializer = PostSerializer(instance, data=request.data, partial=partial, **kwargs)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)    
        return Response(serializer.data)

Upvotes: 4

Ihor Pomaranskyy
Ihor Pomaranskyy

Reputation: 5641

Actually, you don't have to bother with context. There is a much better way to do it:

from rest_framework.fields import CurrentUserDefault

class PostSerializer(serializers.ModelSerializer):

    class Meta:
        model = Post

   def save(self):
        user = CurrentUserDefault()  # <= magic!
        title = self.validated_data['title']
        article = self.validated_data['article']

Upvotes: 89

karthikr
karthikr

Reputation: 99670

You cannot access the request.user directly. You need to access the request object, and then fetch the user attribute.

Like this:

user =  self.context['request'].user

Or to be more safe,

user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
    user = request.user

More on extra context can be read here

Upvotes: 384

Related Questions