Only Owner of the Profile able to Update the data - python

Using class Based (APIView) in Django rest framework for Getting and Patch (Updating) UserInfo data.
views.py
class getUserInfo(APIView):
permission_classes = [permissions.IsAuthenticated]
def get(self, request, format=None):
user = request.user
userinfos = user.userinfo_set.all()
serializer = UserInfoSerializers(userinfos, many=True)
return Response(serializer.data)
def patch(self, request, pk, format=None):
user = UserInfo.objects.get(id=pk)
serializer = UserInfoSerializers(instance=user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
from django.contrib.auth.models import User
from .models import UserInfo
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'first_name', 'username')
class UserInfoSerializers(serializers.ModelSerializer):
user = UserSerializer(many=False, required=True)
class Meta:
model = UserInfo
fields = ('id', 'picture', 'profession', 'user')
Everything is working so far so good. Able to GET and PATCH (Update) logged-in user data.
While Testing the API in Postman, I found out that if User1 is logged in he can change the data of User2 by only using the pk of User2.
urls.py
urlpatterns = [
path('userinfo/', views.getUserInfo.as_view(), name="UserInfo"),
path('userinfo/<str:pk>/', views.getUserInfo.as_view()),
path('api/token/', views.MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('register/', views.RegisterView.as_view(), name='auth_register'),
]
Using rest_framework_simplejwt for Auth
models.py
from django.contrib.auth.models import User
class UserInfo(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
picture = models.ImageField(upload_to="profile_pics", null=True)
profession = models.CharField(max_length=200, null=True)
def __str__(self):
return "%s's Profile Picture" % self.user
Any help would be appreciated

Don't use the primary key to get the user.You are using user = request.user to get the user on get method, use the same mechanism also on update. Then the login user can only update his/her info not others info or another way you can check the user = UserInfo.objects.get(id=pk) is same as the current user request.user . If not you can show an exception.

For Retrieving and Updating an object, you can use RetrieveUpdateAPIView
class GetUserInfo(generics.RetrieveUpdateAPIView):
permission_classes = [IsAuthenticated]
queryset = UserInfo.objects.all()
serializer_class = UserInfoSerializers
def get_object(self):
return self.request.user
Here we are getting an object, it will be called from get_object method. Instead of getting user using PK, we get the current user.
You can use same url for getting and updating the user, just change the method in postman while you hit the api. GET for retrieving and PATCH for partial update.
path('userinfo/', views.GetUserInfo.as_view(), name="UserInfo"),

Related

Can't change user password for Django user model using ModelViewSet

I was using Django user model using the ModelViewSet. When I am making a request to update the password for the current user that is logged in. Although I get a 200 OK response but my password never changes to the new one that I changed.
I also tried making the request from my admin user and when I made the PUT request with the password, it got changed to something else and I was logged out from the django admin panel.
Here is my
views.py
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAuthenticated, IsOwnerOfObject]
authentication_classes = (TokenAuthentication,)
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'password']
extra_kwargs = {
'password' : {
'write_only':True,
'required': True
}
}
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
Token.objects.create(user=user) # create token for the user
return user
urls.py
router = DefaultRouter()
router.register('articles', ArticleViewSet, basename='articles')
router.register('users', UserViewSet, basename = 'users')
urlpatterns = [
path('api/', include(router.urls)),
]
permissions.py
class IsOwnerOfObject(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj == request.user
Here is how I am making the request, with Authorisation token in the Headers field
Response :
As #BrianDestura says, you need call set_password to correctly update it.
class UserSerializer(serializers.ModelSerializer):
# --> Add this method
def update(self, instance, validated_data):
# We Can change the username?
instance.username = validated_data['username']
instance.set_password(validated_data['password'])
instance.save()
return instance

Django - Custom Object Permission View

I'm trying to give shop owner permissions for a view. So I made a file in which I created different permissions. In my permission I first of all check if the user was logged in with a has_permission function. I am now trying to determine if a user actually owns the shop with the has_object_permission function. Unfortunately, I don't feel that my function was performed correctly.
I can always, despite my permission, make a request from any account, shop owner or not.
Here are the models I use:
models.py
class Shop(models.Model):
name = models.CharField(max_length=255)
category = models.ForeignKey(ShopCategory, on_delete=models.SET_NULL, null=True, blank=True)
description = models.TextField(blank=True, null=True)
path = models.CharField(max_length=255, unique=True, null=True, blank=True) # Set a null and blank = True for serializer
mustBeLogged = models.BooleanField(default=False)
deliveries = models.FloatField(default=7)
def __str__(self):
return self.name
class UserShop(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
def __str__(self):
return f"{self.user.name} {self.user.surname} - {self.shop.name}"
Here are my permissions :
utils.py
class IsOwner(BasePermission):
"""
Check if the user who made the request is owner.
Use like that : permission_classes = [IsOwner]
"""
def has_permission(self, request, view):
return request.user and request.user.is_authenticated
def has_object_permission(self, request, view, obj):
try:
user_shop = UserShop.objects.get(user=request.user, shop=obj)
return True
except:
return False
class OwnerView(APIView):
"""
Check if a user is owner
"""
permission_classes = (IsOwner,)
Here is my view :
views.py
class ShopDetail(OwnerView):
"""Edit ou delete a shop"""
def put(self, request, path):
"""For admin or shop owner to edit a shop"""
shop = get_object_or_404(Shop, path=path)
serializer = ShopSerializer(shop, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
And here is my serializer :
serializers.py
class ShopSerializer(serializers.ModelSerializer):
class Meta:
model = Shop
fields = '__all__'
Thank you in advance for your help
As mentioned in the documentation for Custom permissions:
Note: The instance-level has_object_permission method will only be called if the view-level has_permission checks have already
passed. Also note that in order for the instance-level checks to run,
the view code should explicitly call
.check_object_permissions(request, obj). If you are using the
generic views then this will be handled for you by default.
(Function-based views will need to check object permissions
explicitly, raising PermissionDenied on failure.)
You have implemented the put method yourself and get the object yourself instead of using the get_object method (which calls check_object_permissions itself) so check_object_permissions is never called. Instead you should set lookup_field as path in your view class and use get_object:
class ShopDetail(OwnerView):
"""Edit ou delete a shop"""
queryset = Shop.objects.all()
lookup_field = 'path'
def put(self, request, path):
"""For admin or shop owner to edit a shop"""
shop = self.get_object()
serializer = ShopSerializer(shop, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
Also to do this OwnerView should inherit from GenericAPIView:
from rest_framework.generics import GenericAPIView
class OwnerView(GenericAPIView):
"""
Check if a user is owner
"""
permission_classes = (IsOwner,)

Send related image to Django Rest Framework

Hello everyone reading this post. I got such issue.
So, first of all I have such models layout
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
description = models.TextField(max_length=4000, null=True)
liked_profiles = models.ManyToManyField('self', related_name='likes')
disliked_profiles = models.ManyToManyField('self', related_name='dislikes')
class Image(models.Model):
profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='images', max_length=6)
path = models.ImageField(upload_to='profile_images')
So, I want to create a drf endpoint that will receive some kind of image, create it, and link to the profile model. But I really not sure how to implement this(I want to make it with django viewsets).
The main goal is not to provide another viewset class (like ImageViewSet), but to make it part of ProfileViewSet. So now I have such viewset (contains a method to update the description)
class ProfileViewSet(viewsets.ModelViewSet):
queryset = Profile.objects.all()
permission_classes = (IsAuthenticated, )
#action(detail=True, methods=['PUT'])
def set_description(self, request, pk=None):
profile = self.get_object()
serializer = DescriptionSerializer(data=request.data)
if serializer.is_valid():
profile.description = request.data['description']
profile.save()
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
I want to add something like "add_image" method to it.
How can I implement it and is it actually possible(otherwise you can help me to implement it with another viewset)?
I will be extremely grateful for your help
You can do sth similar to your set_description:
#action(
detail=True,
methods=["post"],
serializer_class=ImageSerializer, # write your custom serializer, override save() method and save images from self.context["request"].FILES.items()
)
def create_image(self, request, pk=None):
instance = self.get_object()
serializer = self.get_serializer(instance, data=self.request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)

How to associate user to a post request in Django drf

I have the following :
I am working with DRF, based JWT token.
I want to associate an experiment with a USER, i.e when a post request is arriving I want to be able to save that post request with the Foreginkey it needed for the author by the user whom sent the request.
The POST request is always authenticated and never anonymous, i.e request.user is always exist ( I can see it when debugging)
I tried to add the following
def create(self, request, **kwargs):
request.data["author"] = request.user
serializer = ExperimentsSerializers(data=request.data)
if serializer.is_valid():
serializer.save()
return....
But is_valid return always False ( the only time ts was true, was when I took out the author from the ExperimentsSerializers fields....
will be happy for any leads....
my code attached below
Model.py:
class User(AbstractUser):
pass
def __str__(self):
return self.username
class Experiments(models.Model):
name = models.CharField(max_length=40)
time = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
View.py:
filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
serializer_class = ExperimentsSerializers
queryset = Experiments.objects.all()
filterset_fields = '__all__'
permission_classes = (permissions.IsAuthenticated,)
serializers.py
class ExperimentsSerializers(serializers.ModelSerializer):
class Meta:
model = models.Experiments
fields = '__all__'
You can just pass additional data with save arguments:
def create(self, request, **kwargs):
serializer = ExperimentsSerializers(data=request.data)
if serializer.is_valid():
serializer.save(author=request.user)
Note that you may need to specify author field as read_only so it would not be required in request body:
class ExperimentsSerializers(serializers.ModelSerializer):
class Meta:
model = models.Experiments
fields = '__all__'
read_only_fields = ['author']
One more approach can be to use
HiddenField with default value set to CurrentUserDefault
This way that field will not be exposed at the same time current user will be accessible and other operations can be done on that user context.
author = serializers.HiddenField(default=serializers.CurrentUserDefault())
Something like this:
class ExperimentsSerializers(serializers.ModelSerializer):
author = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = models.Experiments
fields = '__all__'
Reference :
HiddenField - https://www.django-rest-framework.org/api-guide/fields/#hiddenfield
CurrentUserDefault - https://www.django-rest-framework.org/api-guide/validators/#currentuserdefault

Basic: How to use PATCH Method User Model in Django Rest Framework

I have problem with using PATCH Method User Model in Django Rest Framework. Hope your guy helps and save my time.
Urls.py
urlpatterns = [
url(r'^account/edit/$', UserDetailAPIView.as_view({'patch': 'edit'}))
]
Views.py:
class UserDetailAPIView(ReadOnlyModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
#detail_route(methods=['PATCH'])
def edit(self, request):
user_obj = User.objects.get(id=request.user.id)
serializer = UserRegisterSerializer(user_obj, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(status=status.HTTP_400_BAD_REQUEST)
Serializer:
class UserRegisterSerializer(ModelSerializer):
class Meta:
model = User
fields = [
'email',
'first_name',
'last_name'
]
Error:
It's not partial update. It update all fields with let it blank.
When you use patch you need to pass only updated field to your API. For example to update email you need to send this: {'emai':'test#test.com'}. In other word you need not to provide all serializer's fied.

Categories

Resources