How to obtain a user instance using django rest framework - python

Perhaps the question is wrongly worded. I created user profile using Django through the following blocks of code:
models.py
class = Profile (models.Models):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
first name = models.CharField(max_length=50)
•••
serializer.py
class profile_serializer(serializers.ModerlSerializer)
class Meta:
model = Profile
fields = '__all__'
views.py
class profile_view(generics.ListCreateAPIView)
queryset = Profile.objects.all().filter(user=instance)
urls.py
urlspatterns = [path('profile', profile_view.as_view(), name='user_profile)
I definitely do not know how to implement the filter method to ensure that only the logged in user is retrieved. Or is there a better approach to obtain a specific user? If I use Project.objects.all() without the filter I get all the registered user as expected. But I don't know how to retrieve a particular user.

Hmm, I would do something like this:
from rest_framework.response import Response
from rest_framework import status, generics, permissions
class UserView(generics.GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = profile_serializer
def get(self, request):
user = request.user
return Response(profile_serializer(user).data,status=status.HTTP_200_OK)
Basically when a user is authenticated, their user is present in the request.
Here's the UserSerializer, comments was hard to format. (Ironically for a tech forum?)
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
def get(self, instance):
return instance
def patch(self, instance, validated_data):
instance.model_method()
return super().update(instance, validated_data)
def create(self, validated_data):
user = self.context['request'].user
return super().update(user, validated_data)
And here's the profile_serializer:
class profile_serializer(serializers.ModerlSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Profile
fields = ('user', 'first_name', )

Related

how to pass current authenticated user to django rest framework serializer?

I have a model like this:
class Professional(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dummy_text = models.CharField(max_length=300)
a serializer like this:
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
and a view like this one:
class CreateProfessional(generics.CreateAPIView):
serializer_class = ProfessionalSerializer
The thing is, I need to pass the current authenticated user for a given request as the user for my serializer, I'm getting an error because obviously the user field is required as stated in my model, but I can't find an elegant way to do so, how could I go about it?
Set the user as a read_only_fields in the serializer meta. This will prevent accepting the user data from the payload.
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
read_only_fields = ["user"]
Then, override the perform_create(...) method of the view class
class CreateProfessional(generics.CreateAPIView):
serializer_class = ProfessionalSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
You can make validate method with like validate_user and do following code inside.
self.context['view'].request.user()
class CreateProfessional(generics.CreateAPIView):
serializer_class = ProfessionalSerializer
def create(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
serializer.save()
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
def create(self, validated_data):
user = self.context['request'].user
like this you can get your current user
I found a very nice way to do it, I dont wan't to return custom responses, I will let that to the framework, what I did was the following in my view:
def get_serializer(self, *args, **kwargs):
# redefine method to parameterize the serializer
# while leaving the response handling to the
# framework
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
kwargs['context']['user'] = self.request.user
return serializer_class(*args, **kwargs)
and in my serializer:
class EntitySerializer(serializers.ModelSerializer):
class Meta:
model = Entity
fields = '__all__'
extra_kwargs = {'user': {'required': False}}
def create(self, validated_data):
user = self.context['request'].user
entity = Entity.objects.create(user=user, **validated_data)
return entity
You can use ModelViewSet instead of single API Views. ModelViewSet handles all the crud operations automatically.
Let's say you have the model
class Professional(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dummy_text = models.CharField(max_length=300)
and the serializer
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
So you can add a ModelViewSet
class ProfessionalViewSet(ModelViewSet):
queryset = Professional.objects.all()
permission_classes = [IsAuthenticated]
serializer_class = ProfessionalSerializer
this will handle all the crud operations for your model. Following example class will show you the methods that a viewset has.
class UserViewSet(viewsets.ViewSet):
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class.
If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
"""
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
these will handle all the actions. checkout the documentation for more
you'll also have to add the router to handle all the urls automatically which is also covered in the documentation (above link). Following is an example(urls.py).
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = router.urls

I want to create and update UserProfile object with OneToOne User object field and create a api in Django rest framework

I'm new in Django rest framework, I tried my whole day but can't do it,I want to do full crud operation in my UserProfile Model which have a OneToOne field user, User can only update their own profile and in UserProfile create or update user shouldn't update User[username], How can i achieve it Please Help me
*** serializers.py ***
from rest_framework import serializers
from product.models import UserProfile
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
read_only_fields = ['username','password', ]
class UserProfileSerializer(serializers.ModelSerializer):
user = UserSerializer(many=False)
class Meta:
model = UserProfile
fields = "__all__"
def create(self, validated_data):
user_data = validated_data.pop('user')
user_instance = User.objects.get(
username=user_data['username'])
user_instance.save()
user_profile_instance = UserProfile.objects.create(
**validated_data, user=user_instance)
user_profile.save()
return user_profile
*** views.py ***
from django.shortcuts import render
from .serializers import UserProfileSerializer
from rest_framework.views import APIView
from rest_framework import generics, permissions
from rest_framework.response import Response
from rest_framework import status
from django.contrib.auth.models import User
from product.models import UserProfile
# Create your views here.
class CreateUserView(generics.ListCreateAPIView):
serializer_class = UserProfileSerializer
permission_classes = [permissions.IsAuthenticated,]
def get_queryset(self):
user = self.request.user
return UserProfile.objects.filter(user = user)
*** models.py ***
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MaxValueValidator, MinValueValidator
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='user_profile', on_delete=models.CASCADE)
country = models.CharField(max_length=50, default='India')
city = models.CharField(max_length=100, default='')
phone = models.CharField(max_length=15,default='')
image = models.ImageField(upload_to='profile_image', blank=True)
created_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.username
For Authentication you can use token based authentication(like jwt)
and for username you can use read_only=True
no need to send the password for get request
to update profile you need to handle put/post methods
CLEANED Serializers:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
read_only_fields = ['username','password']
class UserProfileSerializer(serializers.ModelSerializer):
# REMOVED all unecessary overrides
user = UserSerializer(read_only=True)
class Meta:
model = UserProfile
fields = "__all__"
Views:
class UserProfileViewSet(viewsets.GenericViewSet,
mixins.UpdateModelMixin):
# Changed inherited class and class NAME !
# I assume that your endpoint is something like /users/me/profile
# I think you want only to update user profile
# Listing or creating profile here is bad - user should have only ONE profile
# and you should do this on user model post_save signal
serializer_class = UserProfileSerializer
permission_classes = [permissions.IsAuthenticated,]
def get_object(self):
return self.request.user.user_profile
This setup will allow you to update profile and only profile data
In your models file you can make signal listener for automatically creating UserProfile object on User object create.
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)

Django REST: Auth user is not passed to the serialiser error - Field is required

I'm not sure what I'm doing wrong, but the authenticated user is not registered in the serialiser.
Models.py
class Post(models.Model):
posted_by = models.ForeignKey('auth.User', on_delete=models.CASCADE)
def save(self, *args, **kwargs):
super(Post, self).save(*args, **kwargs)
Serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
def create(self, validated_data):
post = Post.objects.create(**validated_data)
# extra code to add images
views.py
class PostViewSet(viewsets.ModelViewSet):
serializer_class = PostSerializer
permission_classes = (
permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly, )
def perform_create(self, serializer):
serializer.save(posted_by=self.request.user)
I don't understand why serializer.save(posted_by=self.request.user) doesn't work as intended. It should pass the required information about the field.
When I do a POST request, I get the error that
{
"posted_by": [
"This field is required."
]
}
I think it's something to do with the create method in the serialiser. For some reason, posted_by is not present in validated_data (or something similar). I would like to know what exactly is happening behind the scenes.
just specify posted_by as read only field.
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
read_only_fields = ('posted_by', )

Use different serializer depending on the authentication method in django rest framework

I'm trying to implement a user profile in django rest framework.
Users should be able to request the profile of other users; however, since profiles contain sensitive information, I want to limit the information returned to non-owners and non-authenticated users when they request a profile.
I'm looking for a test that I can run inside my view methods that will determine which serializer to use for that request.
How can I do this?
# models.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='profile')
bio = models.CharField(max_length=100)
# dob is sensitive and should be protected...
dob = models.DateTimeField(blank=True, null=True)
My serializers would look like this:
# serializers.py
# Only for the owner...
class ProfileOwnerSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.ReadOnlyField(source='user.id')
first_name = serializers.ReadOnlyField(source='user.first_name')
last_name = serializers.ReadOnlyField(source='user.last_name')
class Meta:
model = Profile
fields = (
'url',
'id',
'dob', #sensitive
'user',
'first_name',
'last_name', #sensitive
)
#For logged in users...
class ProfileSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.ReadOnlyField(source='user.id')
first_name = serializers.ReadOnlyField(source='user.first_name')
class Meta:
model = Profile
fields = (
'url',
'id',
'bio',
'user',
'first_name',
)
#For everyone else...
class NonAuthProfileSerializer:
...
And I would try to distinguish between them here...
# views.py
class ProfileDetail(APIView):
"""
Retrieve a profile instance.
"""
# Can't user permission_classes bc I want to cater to different classes...
def get_object(self, pk):
try:
return Profile.objects.get(pk=pk)
except Profile.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
profile = self.get_object(pk)
# is_owner = ???
# is_authenticated = ???
# Define the serializer to be ProfileSerializer, ProfileOwnerSerializer, etc.
serializer = CorrectSerializer(
profile,
context={"request": request},
)
return Response(serializer.data)
I don't think it'd be too hard to check if the request was sent by the owner, since I can just cross-reference the profile id.
However, how would I check whether the user was logged in or not? I've tried looking at request.user.auth in the view method, but that seems to be None whether or not the request is logged in.
I think you should be checking with request.user.is_authenticated(). To fill in the blanks:
is_owner = profile.user == request.user
is_authenticated = request.user.is_authenticated()

Can django restframework generics.ListCreateAPIView deal with one-to-one field?

I have a model UserProfile which has a OneToOneField related to Django User model
And I have UserProfileList in views.py
class UserProfileList(generics.ListCreateAPIView):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
I want to post data to UserProfileList
The format like :
{ "username":"username",
"email":"email#email",
"password":"password",
"secret_id":1
}
('username','email','password' is from User model
secret_id is from UserProfile )
and it can save data both in User and UserProfile table
Is it posibble??
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
secret_id=models.IntegerField(default=0)
views.py
from rest_framework import generics
from django.contrib.auth.models import User
from .serializers import UserProfileSerializer, UserSerializer
from account.models import UserProfile
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserProfileList(generics.ListCreateAPIView):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
class UserProfileDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
serializer.py
from django.contrib.auth.models import User
from rest_framework import serializers
from account.models import UserProfile
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserProfile
fields = ('user','secret_id')
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('username','email','password')
Yes, it is possible to achieve this.
In order to have access to the username, email and password fields from within the UserProfileSerializer, you need to define these fields on it, otherwise, the serializer's validation will throw out fields that are not defined on the serializer:
class UserProfileSerializer(serializers.HyperLinkedModelSerializer):
email = serializers.CharField(write_only=True)
username = serializers.CharField(write_only=True)
password = serializers.CharField(write_only=True)
class Meta:
...
Notice that the explicitly defined fields are write_only, so that on read they won't show up.
The above code will assure you that these fields won't be eliminated from validated_attrs in your serializer's .create() and .update() methods.
After this you have 2 options
Option #1
You need to overwrite the .create() method of your UserProfileSerializer in order to create your User model besides the UserProfile model.
class UserProfileSerializer(serializers.HyperLinkedModelSerializer):
# above definitions
def create(self, validated_attrs):
# now you have here the email, username and password arguments
email = validated_attrs.pop('email', None)
username = validated_attrs.pop('username', None)
password = validated_attrs.pop('password', None)
# your creation logic here
user = User.objects.create(...)
user_profile = UserProfile.objects.create(user=user, ...)
Option #2
You can handle the creation of UserProfile from within the User creation process (see handling saving related instances in model manager classes). For this you need to define a custom UserManager class.
class UserManager(models.Manager):
...
def create(self, username, email, secret_id=None):
user = User(username=username, email=email)
user.save()
user_profile = UserProfile(
user=user,
secret_id=secret_id
)
user_profile.save()
return user

Categories