updating user profile using django rest framework api - python

I want to create an API where user can update their profile. In my case, a user can update his/her username and password. To change his/her profile, an API link should be /api/change/usernameOfThatUser. When I use a non-existing username in the link, I still get the userProfileChange API page, and the input boxes are not filled with previous data. How can I solve this?
serializers.py
User = get_user_model()
class UserProfileChangeSerializer(ModelSerializer):
username = CharField(required=False, allow_blank=True, initial="current username")
class Meta:
model = User
fields = [
'username',
'password',
]
def update(self, instance, validated_data):
instance.username = validated_data.get('username',instance.username)
print('instance of username',instance.username)
return instance
views.py
class UserProfileChangeAPIView(UpdateAPIView):
serializer_class = UserProfileChangeSerializer
lookup_field = 'username'
urls.py
url(r'^change/(?P<username>[\w-]+)$', UserProfileChangeAPIView.as_view(), name='changeProfile'),

Maybe try doing something like this instead in your views.py?
from rest_framework import generics, mixins, permissions
User = get_user_model()
class UserIsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.id == request.user.id
class UserProfileChangeAPIView(generics.RetrieveAPIView,
mixins.DestroyModelMixin,
mixins.UpdateModelMixin):
permission_classes = (
permissions.IsAuthenticated,
UserIsOwnerOrReadOnly,
)
serializer_class = UserProfileChangeSerializer
parser_classes = (MultiPartParser, FormParser,)
def get_object(self):
username = self.kwargs["username"]
obj = get_object_or_404(User, username=username)
return obj
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
This will give you all of the existing data for the user based on the username passed in the url. If the username does not exist, it will raise a 404 error. You can also update or delete the object.

Related

Django rest-framwork [views]: users can update only modules they created

So I have a view for my Django app that is used to update/delete user profiles. I'm wondering is there a better way to check if the person who's requesting the change is the same one that's in the url.
request url looks like this /profile/<str:username>
class UserDetail(RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializerFull
permission_classes = [IsAuthenticated]
lookup_field = "username"
def put(self, request, *args, **kwargs):
if str(request.user) == kwargs.get("username"):
return super().put(request, *args, **kwargs)
return Response(
data={"msg": "unauthorized request"}, status=status.HTTP_403_FORBIDDEN
)
def patch(self, request, *args, **kwargs):
if str(request.user) == kwargs.get("username"):
return super().patch(request, *args, **kwargs)
return Response(
data={"msg": "unauthorized request"}, status=status.HTTP_403_FORBIDDEN
)
def delete(self, request, *args, **kwargs):
if str(request.user) == kwargs.get("username"):
return super().delete(request, *args, **kwargs)
return Response(
data={"msg": "unauthorized request"}, status=status.HTTP_403_FORBIDDEN
)
As all of the methods are repeating the same code is there a way to write something just for once and use it everywhere.
Also for other models that are made by the User profile i have the same issue as a lot of the code is repeating itself.
You can implement a custom permission class as follows:
from rest_framework import permissions
class IsRequestingUser(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# Write permissions are only allowed to the owner of the snippet.
return obj.user == request.user
Use this permission class in your class:
class UserDetail(RetrieveUpdateDestroyAPIView):
# other logic
permission_classes = [IsAuthenticated, IsRequestingUser]
# other logic
For more details, you can see the here.
That's what permissions are for.
class IsOwner(IsAuthenticated):
def has_object_permission(self, request, view, obj):
return obj.user == request.user
Something like that will do the trick but you will have to adjust it for your needs.

Sequentially validating individual attributes with Django Rest Framework

I am very new to Django Rest Framework and am confused about coding the following task: when a user on the front-end enters an email address, without any other user information, the API should determine whether or not the email already exists. Likewise, when the user enters a username, without any other user information, the API should determine whether or not the username already exists. This is so the user has feedback on whether their email or username is valid before proceeding to the next sign up stage.
I am aware of the existence of validators, but I don't understand how to use them on partial data.
Here is my serializers.py class
class CustomUserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True)
username = serializers.CharField(min_length=2)
password = serializers.CharField(min_length=8, write_only=True)
first_name = serializers.CharField(min_length=2)
last_name = serializers.CharField(min_length=2)
class Meta:
model = CustomUser
fields = ('pk', 'email', 'username', 'password', 'created_at', 'first_name', 'last_name')
extra_kwargs = {'password': {'write_only': True}}
def validate_email(self, value):
if CustomUser.objects.filter(email=value).exists():
raise serializers.ValidationError("An account already exists with that email address.")
return value
def validate_username(self, value):
if CustomUser.objects.filter(username=value).exists():
raise serializers.ValidationError("An account aready exists with that username.")
return value
def create(self, validated_data):
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
And here is my views.py class
class CustomUserCreate(APIView):
permission_classes = (permissions.AllowAny,)
authentication_classes = ()
def post(self, request, format='json'):
"""
Receives an HTTP request. Serializes and saves a user object.
"""
serializer = CustomUserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
json = serializer.data
return Response(json, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class CustomUserGet(APIView):
def get_user(self, username):
try:
return CustomUser.objects.get(username=username)
except CustomUser.DoesNotExist:
raise Http404
def get(self, request, format='json'):
user = self.get_user(request.user.username)
serializer = CustomUserSerializer(user)
return Response(serializer.data)
class LogoutAndBlacklistRefreshTokenForUserView(APIView):
permission_classes = (permissions.AllowAny,)
def post(self, request):
try:
refresh_token = request.data['refresh_token']
token = RefreshToken(refresh_token)
token.blacklist()
return Response(status=status.HTTP_205_RESET_CONTENT)
except:
return Response(status=status.HTTP_400_BAD_REQUEST)
CustomUser is simply an implementation of AbstractUser.
Again, this successfully validates when a user is created with complete data. However, I want to validate on partial data, such as a lone email address and lone username without the presence of other attributes such as first_name, last_name, etc. What is the proper way of approaching this?

Django Rest Framework creating Profile instance instead of updating Profile

I want to update the Profile instance for a user, by sending data to the url update_profile/
I am using:
curl -X PUT -H "Authorization: Token sometoken" -d "name=somename&age=20&user=1" 127.0.0.1:8000/update_profile/
and the error I get is: {"user":["This field must be unique."]}
It seems like the error comes because the request creates a new instance of Profile, when there is already another instance of Profile with that same user. But I just want to update the current Profile instance for the user. Any idea why my code does try to create a new instance instead of updating it?
views.py
class UserProfileChangeAPIView(generics.UpdateAPIView, mixins.UpdateModelMixin):
permission_classes = (
permissions.IsAuthenticated,
UserIsOwnerOrReadOnly,
)
serializer_class = UpdateProfileSerializer
parser_classes = (MultiPartParser, FormParser,)
def get_queryset(self, *args, **kwargs):
queryset = Profile.objects.filter(user=self.request.user)
return queryset
def get_object(self):
self.request.user.profile
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
def update(self, request, pk=None):
try:
self.request.user.profile
except Http404:
return Response(
{'detail': 'Not found'}, status=status.HTTP_404_NOT_FOUND)
return super(UserProfileChangeAPIView, self).update(request, pk)
urls.py
url(r'^update_profile/$', UserProfileChangeAPIView.as_view(), name='update_profile'),
serializers.py
class UpdateProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = [
'name',
'age',
'user',
]
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100, null=True)
age = models.PositiveIntegerField(null=True)
You shouldn't include a user in the post data as you're already getting the profile for the current logged in user in get_object() and get_queryset()
Because the relationship between Profile and User is a OnetoOneField, there can't be more than one Profile linked to the same User.
I expect (from the error you've received) what's happening here is that your logged in as one User (e.g. id 3) and then trying to update the Profile to point to User id 1.
I was able to solve the problem like this and no need for pk in the url:
serializers.py
class UpdateProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = [
'name',
'age',
]
views.py
class ProfileUpdateAPIView(UpdateAPIView):
serializer_class = UpdateProfileSerializer
def get_object(self):
queryset = Profile.objects.filter(user=self.request.user)
obj = queryset[0]
return obj
urls.py:
url(r'^update/$', ProfileUpdateAPIView.as_view(), name='update'),
curl:
curl -X PUT -H "Authorization: Token sometoken" -d "name=newname&age=20" 127.0.0.1:8000/update/

Retrieving current user inside Serializer Method in Django Rest API

I have a serializer for user profiles in Django Rest:
class UserProfileSerializer(serializers.ModelSerializer):
......................
......................
status = serializers.SerializerMethodField()
def get_status(self, obj):
user = self.context['request'].user
if obj.user.userprofile in user.followed_userprofiles_set.all():
return "following"
else:
return "notfollowing"
class Meta:
model = UserProfile
fields = (...., 'status',...)
And I have two views that use this serializer:
class Followers(APIView):
def get(self, request, format=None):
#user who follow current user
users = request.user.userprofile.followers.all()
userprofiles= UserProfile.objects.filter(user__in=users)
serializer = UserProfileSerializer(userprofiles, many=True)
return Response(serializer.data)
and
class Friends(mixins.ListModelMixin, generics.GenericAPIView):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
permission_classes = (permissions.IsAuthenticated,)
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def get_queryset(self):
.................
.................
return queryset
One view is using APIView and other is using genericAPIView. When i request from genericAPIView, its working properly. But when i request from APIView, its giving me key error. How to retrieve the current user inside serializer method when APIView is used?
Since you are manually instantiating the UserProfileSerializer in your APIView class without passing the context, KeyError exception gets raised.
You should pass the request in context parameter when instantiating the UserProfileSerializer in your APIView.
class Followers(APIView):
def get(self, request, format=None):
#user who follow current user
users = request.user.userprofile.followers.all()
userprofiles= UserProfile.objects.filter(user__in=users)
context = {'request':request} # prepare serializer context
serializer = UserProfileSerializer(userprofiles, many=True, context=context) # pass context
return Response(serializer.data)

request.user in Django model

I am using Django models to create the fields for a form. I would like to have the user's username automatically detected in and be filled out, this way I can hide it in my form (instead of having them choose their username from a long list that has everyones username). To do this I am using:
current_user = request.user
and then setting the default to current_user. However, I keep getting this error:
NameError: name 'request' is not defined
I'm assuming you can't use requests in Django models, but is there anyway to get around this? Here is the relevant sections of my models.py file:
class StockTickerSymbol(models.Model):
StockName = models.CharField(max_length=7, unique=True)
current_user = request.user
user = models.ForeignKey(User, default=current_user)
Anyone know how I can use requests in my models, or somehow call the variable current_user?
Here you haven't imported request in that model class scope. This is how you can get user:
# model
class StockTickerSymbol(models.Model):
StockName = models.CharField(max_length=7, unique=True)
user = models.ForeignKey(User)
def save(self,**kwargs):
if kwargs.has_key('request') and self.user is None:
request = kwargs.pop('request')
self.user= request.user
super(StockTickerSymbol, self).save(**kwargs)
#views:
def post(self, request):
if form.is_valid():
sts=StockTickerSymbol()
sts.StockName= form.cleaned_data['StockName']
if form.cleaned_data['user'] is None: #null check
sts.save(request=request)
else:
sts.user= form.cleaned_data['user']
sts.save(request=request)
For modelform:
class SomeForm(forms.ModelForm):
...
def save(self, commit=True ,*args, **kwargs):
request = None
if kwargs.has_key('request'):
request = kwargs.pop('request')
m = super(SomeForm, self).save(commit=False, *args, **kwargs)
if m.user is None and request is not None:
m.user= request.user
m.save()
in views:
def post(self, request):
if form.is_valid():
form.save(request=request)
return ...

Categories

Resources