Django update api - python

I ma creating a user profile update api via django:
in urls:
url(r'^/api/users/(?P<user_id>[0-9]+)$', UserView.as_view(), name='user_profile'),
And my view:
class UserView(APIView):
def patch(self, request, user_id):
# logging.info('user Id: %s' % user_id)
logging.info('in patch...')
user = User.objects.get(id=user_id)
serializer = UserSerializer(user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_200_OK)
return Response(status=status.HTTP_400_BAD_REQUEST)
why patch def not called at all?! (I get 504 method not allowed)
my request is:
patch > http://localhost:8000/api/users/2
When i'm deleting the user_id argument in view, it works, but i need to get the user id in path.

def patch(self, request, user_id):
# logging.info('user Id: %s' % user_id)
logging.info('in patch...')
user = User.objects.get(id=2)
serializer = UserSerializer(instance=user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_200_OK)
return Response(status=status.HTTP_400_BAD_REQUEST)
You must provide instance eq: serializer = UserSerializer(instance=user, data=request.data, partial=True)

try this.
class UserView(APIView):
def patch(self, request, *args, **kwargs):
# try to get user_id from kwargs.get('user_id', None)

Related

DRF Custom Update password got KeyError: 'request'

When I want to update password received error,
user = self.context['request'].user
KeyError: 'request'
Can someone help me with it?
serializer:
Custom serializer
class UserPasswordChangeSerializer(serializers.Serializer):
old_password = serializers.CharField(required=True)
password = serializers.CharField(required=True)
class Meta:
model = User
fields = ('old_password', 'password')
def validate_old_password(self, data):
user = self.context['request'].user
if not user.check_password(data):
raise serializers.ValidationError(
{'old_password': 'Wrong password.'}
)
return data
def update(self, instance, validated_data):
instance.set_password(validated_data['password'])
return super().update(instance)
My action view
My action view
#action(methods=['patch'], detail=True)
def change_password(self, request, *args, **kwargs):
user = self.get_object()
user.serializer = UserPasswordChangeSerializer(data=request.data)
user.serializer.is_valid(raise_exception=True)
user.serializer.save()
return Response(status=status.HTTP_204_NO_CONTENT)
You need to pass request as context variable to the serializer
#action(methods=['patch'], detail=True)
def change_password(self, request, *args, **kwargs):
user = self.get_object()
user.serializer = UserPasswordChangeSerializer(data=request.data,
context={'request': request})
user.serializer.is_valid(raise_exception=True)
user.serializer.save()
return Response(status=status.HTTP_204_NO_CONTENT)
Reference: Including extra context in Serializer

Refactoring views in Django REST framework

I am very new to Python and Django. I have this app that returns 4 different types of transport routes (In the code I only showed two, cause they basically are the same...).
These 4 views use the same class-based views, but only the models' names are different. As they all return the same functionality(get, post, put and delete) I ended up repeating the same code over and over again.
Is there any way I can refactor it simpler?
Any help is appreciated! Thank you :)
views.py
********* tube view ***********
class TubeListView(APIView):
def get(self, _request, format=None):
tubeRoutes = TubeRoute.objects.all()
serialized_with_user = NestedTubeRouteSerializer(tubeRoutes, many=True)
return Response(serialized_with_user.data)
def post(self, request, format=None):
request.data['traveler'] = request.user.id
serializer = TubeRouteSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)
class TubeDetailView(APIView):
def get(self, _request, pk, format=None):
tubeRoute = TubeRoute.objects.get(pk=pk)
serialized_with_user = NestedTubeRouteSerializer(tubeRoute)
return Response(serialized_with_user.data)
def put(self, request, pk, format=None):
request.data['traveler'] = request.user.id
tubeRoute = self.get_object(pk)
if tubeRoute.owner.id != request.user.id:
return Response(status=status.HTTP_401_UNAUTHORIZED)
updated_serializer = TubeRouteSerializer(tubeRoute)
if updated_serializer.is_valid():
updated_serializer.save()
return Response(updated_serializer.data, status=status.HTTP_200_OK)
return Response(updated_serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)
def delete(self, request, pk, format=None):
tubeRoute = self.get_object(pk)
if tubeRoute.owner.id != request.user.id:
return Response(status=status.HTTP_401_UNAUTHORIZED)
tubeRoute.delete()
return Response(status=status.HTTP_200_OK)
********* bus view ***********
class BusListView(APIView):
def get(self, _request, format=None):
busRoutes = BusRoute.objects.all()
serialized_with_user = NestedBusRouteSerializer(busRoutes, many=True)
return Response(serialized_with_user.data)
def post(self, request, format=None):
request.data['traveler'] = request.user.id
serializer = BusRouteSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)
class BusDetailView(APIView):
def get(self, _request, pk, format=None):
busRoute = BusRoute.objects.get(pk=pk)
serialized_with_user = NestedBusRouteSerializer(busRoute)
return Response(serialized_with_user.data)
def put(self, request, pk, format=None):
request.data['traveler'] = request.user.id
busRoute = self.get_object(pk)
if busRoute.owner.id != request.user.id:
return Response(status=status.HTTP_401_UNAUTHORIZED)
updated_serializer = BusRouteSerializer(busRoute)
if updated_serializer.is_valid():
updated_serializer.save()
return Response(updated_serializer.data, status=status.HTTP_200_OK)
return Response(updated_serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)
def delete(self, request, pk, format=None):
busRoute = self.get_object(pk)
if busRoute.owner.id != request.user.id:
return Response(status=status.HTTP_401_UNAUTHORIZED)
busRoute.delete()
return Response(status=status.HTTP_200_OK)
You should take a look to these class based views in DRF.
For instance, the following code should be enough to replace your first TubeListView:
from rest_framework import generics
class TubeListView(generics.ListCreateAPIView):
queryset = TubeRoute.objects.all()
serializer_class = NestedTubeRouteSerializer
def post(self, request, *args, **kwargs):
request.data['traveler'] = request.user.id
return super().post(self, request, *args, **kwargs)
If you don't need any special behavior, you don't have to redefine get, post, etc methods. But if you need to change data, for instance in your POST method, you can do your stuff and then call the usual behavior of the superclass with super().post(self, request, *args, **kwargs)
I would recommend checking Viewsets in Django Rest Framework, specifically ModelViewset.
The actions provided by the ModelViewSet class are .list(), .retrieve(), .create(), .update(), .partial_update(), and .destroy() which coincide with the following:
get ----> .retrieve()
list ----> .list()
post ----> .create()
patch ----> .partial_update()
put ----> .update()
delete ----> .destroy()
Let me provide a case using the Bus Views
The Modelviewset form would be:
class BusViewset(ModelViewset):
queryset = BusRoute.objects.all()
serializer_class = NestedBusRouteSerializer
def get_queryset(self):
return self.queryset.filter(
owner__id=self.request.user.id
)
For the permission checks you implemented, Django Rest Framework has a Permissions System that takes care of that. For you own use case, a custom permission would be sufficient.

How to get user object from request in django rest framework?

I want to get the password of curent user from request body and verify it.
I am trying to get user object from request like this:
class UserPasswordUpdateAsAdminViewSet(APIView):
def patch(self, request, pk):
serializer = ResetPasswordAsAdminSerializer(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
serializer.update_password()
return Response(status=status.HTTP_200_OK, data={'success': True})
and below is my serializer:
class ResetPasswordAsAdminSerializer(serializers.ModelSerializer):
new_password = serializers.CharField(write_only=True)
password = serializers.CharField(write_only=True)
def validate_password(self, attrs):
self.user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
self.user = request.user
if not self.user or not self.user.check_password(attrs['password']):
raise CustomAPIException(
status_code=status.HTTP_401_UNAUTHORIZED,
message='Invalid password provided',
error_code=None
)
def validate_new_password(self, password):
if not re.match(REGEX['password'], str(password)):
raise CustomAPIException(
status_code=status.HTTP_409_CONFLICT,
message=None,
error_code='password_policy_mismatch'
)
return password
def update_password(self):
self.user.set_password(self.validated_data['new_password'])
self.user.save()
return
class Meta:
model = User
fields = ('password', 'new_password')
But on hitting the api, I am getting the following error:
string indices must be integers
What am I doing wrong?
You don't have to do this in the serializer itself, it can be done in the views pretty easily take a look at this example
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
if not user.check_password(serializer.data.get('old_password')):
return Response({'old_password': ['Wrong password.']},
status=status.HTTP_400_BAD_REQUEST)
# set_password also hashes the password that the user will get
user.set_password(serializer.data.get('new_password'))
user.save()
return Response({'status': 'password set'}, status=status.HTTP_200_OK)
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)

How fix test for change password view (Django REST Framework)?

In view, the test does not take the request.data as an element of DRF.
I have this view:
class ChangePasswordView(UpdateAPIView):
serializer_class = ChangePasswordSerializer
model = UserInfo
permission_classes = (IsAuthenticated,)
def get(self, request, *args, **kwargs):
return Response(status=200)
def put(self, request, *args, **kwargs):
self.object = self.request.user
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
if not self.object.check_password(serializer.data.get("old_password")):
return Response(
{"old_password": ["Wrong password"]},
status=400
)
self.object.set_password(serializer.data.get("new_password"))
self.object.save()
return Response("Success", status=200)
return Response(serializer.errors, status=400)
and I have this test method:
def test_change_password(self):
client = APIClient()
self.client.post('/api/user/login/', self.data, follow=True)
self.client.login(username="testuser", password="secret")
request = client.put('/api/user/change_password/', self.new_data)
self.assertEqual(
request, '<Response status_code=200, "application/json">'
)
where data:
def setUp(self):
self.data = {
'username': 'testuser',
'password': 'secret'
}
self.new_data = {
'old_password': 'secret',
'new_password': 'other_secret'
}
User.objects.create_user(**self.data)
Why does the test stop at reqest.data in serializer = self.get_serializer(data=request.data)?
I guess that you are getting the user wrong.
This might solve your issue:
from rest_framework import status
class ChangePasswordView(UpdateAPIView):
serializer_class = ChangePasswordSerializer
model = UserInfo
permission_classes = (IsAuthenticated,)
def get(self, request, *args, **kwargs):
return Response(status=status.HTTP_200_OK)
def post(self, request, *args, **kwargs):
user = request.user
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
if not self.object.check_password(serializer.data.get("old_password")):
msg = _("Wrong old password.")
raise ValidationError(msg)
user.set_password(serializer.data.get("new_password"))
user.save()
return Response("Success", status=status.HTTP_200_OK)
msg = _('Something went wrong while reading the passwords')
raise ValidationError(msg)
I am using the imported status from rest_framework in order to handle the error codes more easily.
Is there any specific reason you are using put? I have changed it to post. When making this API call (change password), you should use post.
You can easily get the user with request.user and then use the variable user inside put function to set the password and save it.
Regarding the test case, this could be a help and/or work for you. If it doesn't work, you might need to change it a bit.
from rest_framework import status
def test_change_password(self):
client = APIClient()
self.client.post('/api/user/login/', self.data, follow=True)
self.client.login(username="testuser", password="secret")
response = self.post('/api/user/change_password/', self.new_data)
self.assertEqual(response.status_code, status.HTTP_200_OK)

PUT and GET single element not working at DJANGO using rest-api. (BUT POST & GET WORKS!)

I'm trying to work with django & rest api. The get & post functions are working for me, (for example at /users/).
But when i'm trying to access a specific user, I receive this error:
TypeError at /users/1 put()
missing 1 required positional argument: 'pk'
I think it has something to do with the urls.
Code of UserList, getElement and put doesn't work
class UserList(APIView):
def get_object(self, pk):
try:
return User.objects.get(pk=pk)
except User.DoesNotExist:
raise Http404
''' Returns list of all users in json format '''
def get(self, request):
#print("In get")
#print("pk: "+pk)
users = User.objects.all()
serializedUsers = UserSerializer(users, many=True)
return Response(serializedUsers.data)
def getElement(self, request, pk):
print("Inside put. pk: " + str(pk))
user = self.get_object(pk)
serializer = UserSerializer(user, data=request.data)
if serializer.is_valid():
#serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
''' Enables to add new user to DB '''
def post(self, request):
print("In post USER")
response = Response({"message": "Added user", "data": request.data})
deserializedUser = UserSerializer(data=request.data)
if deserializedUser.is_valid(raise_exception=True):
deserializedUser.save()
return response
else:
print("Error occured")
return Response(deserializedUser.errors, status=status.HTTP_400_BAD_REQUEST)
''' Enables to update existing user in the DB '''
def put(self, request, pk, format=None):
print("Inside put. pk: " + str(pk))
user = self.get_object(pk)
serializer = UserSerializer(user, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
This is a part in the urls file:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^users/', views.UserList.as_view()),
url(r'^users/(?P<pk>[0-9]+)$', views.UserList.as_view()),
url(r'^routes/', views.RouteList.as_view()),
url(r'^runs/', views.RunList.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
I've seen the r'^users/(?P[0-9]+)$' somewhere so I guess it's good, But i'm not sure which function I need call.
Thank you all in advance :)

Categories

Resources