DRF how to update email with #action? - django

When I try to update my authenticated user but it's create new user. Where is problem?
View
class UserViewSet(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = UserSerializer
def get_object(self):
return self.request.user
Action
#action(methods=['PATCH'], detail=False)
def change_email(self, *args, **kwargs):
user = self.get_object()
serializer = serializers.UserEmailSerializer(data=self.request.data, context={'request': self.request})
serializer.is_valid(raise_exception=True)
serializer.save()
self.send_email(user.email)
return Response({'Confirm yours new email'}, status=status.HTTP_200_OK)
Serilializer for action
class UserEmailSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'email')
read_only_fields = ('id',)

Related

Check if object parent user == request.user on child object create/update

I want to check if parent object user is request.user to correctly add child amd grandchild object permissions.
Wanna figure out how to do it properly in Django & DRF.
Models:
class Parent(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Child(models.Model):
parent = models.ForeignKey(Parent, on_delete=models.CASCADE, related_name='childs')
class SecondChild(models.Model):
parent = models.ForeignKey(Child, on_delete=models.CASCADE, related_name='secondchilds')
Views:
class ParentViewSet(viewsets.ModelViewSet):
queryset = Parent.objects.all()
serializer_class = ParentSerializer
permission_classes = [permissions.IsAuthenticated, IsOwner]
def perform_create(self, serializer):
serializer.save(user=self.request.user)
def get_queryset(self):
user = self.request.user
if user.is_superuser:
return Parent.objects.all()
return Parent.objects.filter(user=user).prefetch_related('childs', 'childs__secondchilds')
class ChildViewSet(viewsets.ModelViewSet):
queryset = Child.objects.all()
serializer_class = ChildSerializer
permission_classes = [permissions.IsAuthenticated]
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
parent_id = self.request.data.get('parent_id')
parent_instance = Parent.objects.filter(id=parent_id).first()
if not serializer.is_valid(raise_exception=True):
print(serializer.errors)
serializer.save(parent=parent_instance)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def get_queryset(self):
user = self.request.user
if user.is_superuser:
return Parent.objects.all()
return Child.objects.filter(parent__user=user).prefetch_related('SecondChild')
you can do the checking on your custom permission class, then add the it to your view's permission_classes list
permissions
class IsParentOwnerOrReadOnly(permissions.BasePermission):
def has_permission(self, request, view):
parent_id = int(request.data.get('parent_id'))
parent_instance = Parent.objects.filter(id=parent_id).first()
return (request.method in permissions.SAFE_METHODS or
(parent_instance and request.user == parent_instance.user))
views
class ChildViewSet(viewsets.ModelViewSet):
...
permission_classes = [permissions.IsAuthenticated, IsParentOwnerOrReadOnly]
...

Allow partial update in serializer_class in Django REST

I am trying to partially update my ProfileSerializer when i am making PATCH request. However i am not able to make it beacuse by default Serializer doesn't allow partial changes to be made. I am using Django Rest Framwork UpdateModelMixin to handle my patch request. Where can i set partial=True in my case?
View:
class ProfileViewPartialUpdate(GenericAPIView, UpdateModelMixin):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
lookup_field = 'token'
lookup_url_kwarg = 'pk'
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
Serializer:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('token', 'bio', 'name', 'email', 'sport', 'location', 'image')
View:
from rest_framework import generics
class ProfileViewPartialUpdate(generics.RetrieveUpdateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
lookup_field = 'pk'
Serializer:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('token', 'bio', 'name', 'email', 'sport', 'location', 'image')
Look at this snippet code from one of my projects. You can modify accordingly. Add this to ProfileViewPartialUpdate class instead of patch.
def partial_update(self, request, slug):
user = self.request.user
get_blog = Blog.objects.get(slug=slug)
instance = self.get_object()
serializer = BlogSerializer(instance, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(data=serializer.data, status=HTTP_201_CREATED)
return Response(data="wrong parameters", status=HTTP_400_BAD_REQUEST)
Hope this might help

No PUT method in DRF using ModelViewset

I am using Django 2.2 with Django Rest Framework 3.7.
I have a class like this:
class UserViewSet(viewsets.ModelViewSet):
permission_classes = [AllowAny]
serializer_class = UserSerializer
queryset = User.objects.all()
def update(self, request, *args, **kwargs):
import pdb;pdb.set_trace()
I've created UserSerializer like this:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'email', 'name', 'password')
write_only_fields = ('password',)
read_only_fields = ('id',)
def create(self, validated_data):
user = User.objects.create(
email=validated_data['email'],
name=validated_data['name'],
)
user.set_password(validated_data['password'])
user.save()
return user
def update(self, instance, validated_data):
print('lalala from serialzier')
import pdb;pdb.set_trace()
instance.username = validated_data['username']
instance.save()
return instance
Only allowed methods being shown are - Allow: GET, POST, HEAD, OPTIONS
I wonder why I am unable to perform actions like PUT, DELETE, RETRIEVE. These are by default supported by using ModelViewset as per documentation.
In shown code neither serializer's update() nor views.py update() method getting called. Any hint would be appreciated.
Does this answer your question?
class UserViewSet(viewsets.ModelViewSet):
permission_classes = [AllowAny]
serializer_class = UserSerializer
queryset = User.objects.all()
def put(self, request, id, format=None):
...
def delete(self, request, id, format=None):
...

django rest framework RetrieveUpdateAPIView

I come from Vietnam.
I want to update User by Django Rest FrameWork. I can update User by 'pk'. But I can't update User by 'username'. I hope that everyone help me. Thank you so much.
serializers.py
class UserDetailSerializer(serializers.ModelSerializer):
url_update = serializers.HyperlinkedIdentityField(view_name='api:UserUpdateAPIView', lookup_field='username')
class Meta:
model = User
fields = ('url_update', 'username', 'email', 'user_permissions', 'is_staff', 'groups', 'last_login')
class UserUpdateSerialier(serializers.ModelSerializer):
password = serializers.CharField(
style={'input_type': 'password'}
)
class Meta:
model = User
fields = ('pk', 'username', 'password')
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.set_password(validated_data.get('password', instance.password))
instance.save()
return instance
views.py
class UserDetailAPIView(generics.RetrieveAPIView):
permission_classes = (permissions.IsAdminUser,)
serializer_class = UserDetailSerializer
#queryset = User.objects.all()
lookup_field = 'username'
def get_object(self):
username = self.kwargs["username"]
return get_object_or_404(User, username=username)
class UserUpdateAPIView(generics.RetrieveUpdateAPIView):
permission_classes = (permissions.IsAdminUser,)
#queryset = User.objects.all()
serializer_class = UserUpdateSerialier
def get_object(self):
username = self.kwargs["username"]
return get_object_or_404(User, username=username)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
urls.py
url(r'^$', views.UserListAPIView.as_view(), name='UserListAPIView'),
url(r'^(?P<username>.*)/$', views.UserDetailAPIView.as_view(), name='UserDetailAPIView'),
url(r'^(?P<username>.*)/update/$', views.UserUpdateAPIView.as_view(), name='UserUpdateAPIView'),
HTTP 404 Not Found
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"detail": "Not found."
}
Try editing your view like this,
class UserUpdateAPIView(generics.RetrieveUpdateAPIView):
permission_classes = (permissions.IsAdminUser,)
serializer_class = UserUpdateSerialier
lookup_field = 'username'
def get_object(self):
username = self.kwargs["username"]
return get_object_or_404(User, username=username)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
You need to set lookup_field = 'username' to the UserUpdateAPIView just like you did with the UserDetailAPIView

How I can pass over a field validation in a serializer in Django Rest Framework

I have a Location model and I need to create a Location without specifying a user, if user in empty then the user will be placed in request.user in the viewset.
This is my model:
class Location(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
serializer:
class LocationSerializer(serializers.ModelSerializer):
def is_valid(self):
if not 'user' in self.init_data:
# avoid this validation.. I manage this in the viewset
pass
return not self.errors
class Meta:
model = Location
and viewset
class LocationViewSet(ModelViewSet):
"""
API endpoint that allows location to be created or viewed.
"""
model = Location
serializer_class = LocationSerializer
renderer_classes = (JSONRenderer, JSONPRenderer)
def get_queryset(self):
return self.request.user.locations.filter(deleted=False)
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.DATA, files=request.FILES)
if serializer.is_valid():
self.pre_save(serializer.object)
if not self.object.user:
self.object.user = request.user
self.object = serializer.save(force_insert=True)
self.post_save(self.object, created=True)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
Thanks for any suggestion
You can use required=False in serializer:
class LocationSerializer(serializers.ModelSerializer):
user = serializers.RelatedField(required=False)
EDIT
Also you can simplify your viewset with:
class LocationViewSet(ModelViewSet):
def pre_save(self, obj):
if obj.user_id is None:
obj.user = self.request.user
This avoid copying code from DRF core.