django rest framework url arguments with patch not working - python

Edit
I suspect the whole problem with my UpdateApiView is with the url. No matter how I change it, will return 404.
url(r'verify-phone/(?P<phone_id>^\d+)$', view.VerifyPhone.as_view(), name='verify-phone'),
it returns
{
"detail": "Not found."
}
[18/Apr/2016 01:39:02] "PATCH /api/verify-phone/phone_id=00980 HTTP/1.1" 404 4941
Why?
views.py
class VerifyPhone(generics.UpdateAPIView):
permission_classes = (AllowAny,)
serializer_class = serializers.VerifyPhoneSerializer
allowed_methods = ['PATCH']
lookup_field = 'phone_id'
def get_queryset(self):
phone_id = self.request.query_params.get('phone_id', None)
queryset = User.objects.filter(phone_id=phone_id)
return queryset
def update(self, request, *args, **kwargs):
print('inside update')
print(request.data)
partial = kwargs.pop('partial', False)
instance = self.get_object()
print(instance)
serializer = self.get_serializer(instance, data=request.data, partial=partial)
print(serializer)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
print('done perform update')
return Response(serializer.data)
serializers.py
class VerifyPhoneSerializer(serializers.ModelSerializer):
regex = r'\d+'
verification_code = serializers.RegexField(regex, max_length=7, min_length=7, allow_blank=False)
phone_id = serializers.HyperlinkedRelatedField(view_name='verify-phone', lookup_field='phone_id', read_only=True)
class Meta:
model = User
fields = ('verification_code', 'phone_id')
def validate(self, data):
verification = api.tokens.verify(data['phone_id'], data['verification_code'])
if verification.response.status_code != 200:
raise serializers.ValidationError("Invalid verification code.")
return data
def update(self, instance, validated_data):
instance.phone_number_validated = True
instance.save()
return instance
Second question Is this correct to get phone_id from the views?
phone_id = serializers.HyperlinkedRelatedField(view_name='verify-phone', lookup_field='phone_id', read_only=True)

Looking at your api url def, I think you should call:
/api/verify-phone/00980
instead of
/api/verify-phone/phone_id=00980
I also think something is wrong with the url def itself (the ^ before \d):
url(r'verify-phone/(?P<phone_id>^\d+)$', view.VerifyPhone.as_view(), name='verify-phone')
should be
url(r'verify-phone/(?P<phone_id>\d+)$', view.VerifyPhone.as_view(), name='verify-phone')
or
url(r'verify-phone/(?P<phone_id>\d{5})$', view.VerifyPhone.as_view(), name='verify-phone')

Related

How to raise 404 as a status code in Serializer Validate function in DRF?

I have written a validate() function inside my serializer. By default, Serializer Errors return 400 as a status code. But I want to return 404. I tried this:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = models.MyClass
fields = "__all__"
def validate(self, data):
current_user = self.context.get("request").user
user = data.get("user")
if user!=current_user:
raise ValidationError({'detail': 'Not found.'}, code=404)
return data
But it still returns 400 as a response in status code.
How to do it?
You can do it from view by handling the serializes validation:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = models.MyClass
fields = "__all__"
def validate(self, data):
current_user = self.context.get("request").user
user = data.get("user")
if user != current_user:
raise serializers.ValidationError(
{'detail': 'Not found.'},
code=404,
)
return data
class MyView(APIView):
def post(self, request):
serializer = MySerializer(
data=request.data,
context={'request': request},
)
if not serializer.is_valid():
return Response(serializer.errors, status=404)
return Response(serializer.data)
Alternately you can do it by defining own exception handler:
def my_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is not None and response.status_code == 400:
response.status_code = 404
return response
class MyView(APIView):
exception_handler = my_exception_handler
def post(self, request):
serializer = MySerializer(
data=request.data,
context={'request': request},
)
serializer.is_valid(raise_exception=True)
return Response(serializer.data)
But as you said, if you want to do it from serializer, then you have to define custom exception and raise it from serialize :
class NotFoundError(serializers.ValidationError):
def __init__(self, detail):
super().__init__(detail, code=404)
class MySerializer(serializers.ModelSerializer):
class Meta:
model = models.MyClass
fields = "__all__"
def validate(self, data):
current_user = self.context.get("request").user
user = data.get("user")
if user != current_user:
raise NotFoundError({'detail': 'Not found.'})
return data
This is the Django source code that handles validation errors.
def is_valid(self, *, raise_exception=False):
# This implementation is the same as the default,
# except that we use lists, rather than dicts, as the empty case.
assert hasattr(self, 'initial_data'), (
'Cannot call `.is_valid()` as no `data=` keyword argument was '
'passed when instantiating the serializer instance.'
)
if not hasattr(self, '_validated_data'):
try:
self._validated_data = self.run_validation(self.initial_data)
except ValidationError as exc:
self._validated_data = []
self._errors = exc.detail
else:
self._errors = []
if self._errors and raise_exception:
raise ValidationError(self.errors)
return not bool(self._errors)
Django checks for ValidationError, then takes the details from that error object and re-creates with a 400 status code, which is why your code does not work.
So, from your view class, catch the django error and change the status code as shown.
class NotesList(mixins.CreateModelMixin, mixins.ListModelMixin, generics.GenericAPIView):
serializer_class = MySerializer
def get_queryset(self):
return models.MyClass.objects.filter(owner=self.request.user)
def post(self, request):
try:
return self.create(request)
except ValidationError as exc:
exc.status_code = 404
raise exc

How can i call own method in django class based api views

I want to create account using phone number as a username and generate otp code . now i want to validate my phone number. I create a method is_phone_valid to validate my phone number but call it by post method but i am getting an error
This is my views.py
class GetPhoneNumber(CreateAPIView):
queryset = TempRegistration.objects.all()
serializer_class = AccountsSerializer
def is_phone_valid(phone_number):
if phone_number:
MOBILE_REGEX = re.compile('^(?:\+?88)?01[15-9]\d{8}$')
if MOBILE_REGEX.match(phone_number):
return True
else:
return False
else:
return False
def post(self, request, *args, **kwargs):
return self.is_phone_valid(phone_number)
models.py
class TempRegistration(models.Model):
phone_number = models.CharField(max_length=45)
otp_code = models.CharField(max_length=6)
def __str__(self):
return self.phone_number
Seems like you are using Django REST Framework. If so, use field-level--DRF doc validation of DRF Serializers, as below
# views.py
class GetPhoneNumber(CreateAPIView):
queryset = TempRegistration.objects.all()
serializer_class = AccountsSerializer
# serializers.py
class AccountsSerializer(serializers.ModelSerializer):
def validate_phone_number(self, phone_number):
MOBILE_REGEX = re.compile('^(?:\+?88)?01[15-9]\d{8}$')
if MOBILE_REGEX.match(phone_number):
return phone_number
else:
raise serializers.ValidationError('No. not matching')
class Meta:
model = TempRegistration
fields = '__all__'
Note: You don't need to override the post(...) method of CreateAPIView class.
The traceback you have posted explains that it can't find the phone_number you are passing to self.is_phone_valid(phone_number) in your post method.
Secondly, your question is how to call the is_phone_valid from inside the class? You need to make pass self as the first arg of this method.
class GetPhoneNumber(CreateAPIView):
queryset = TempRegistration.objects.all()
serializer_class = AccountsSerializer
def is_phone_valid(self, phone_number):
if phone_number:
MOBILE_REGEX = re.compile('^(?:\+?88)?01[15-9]\d{8}$')
if MOBILE_REGEX.match(phone_number):
return True
else:
return False
else:
return False
def post(self, request, *args, **kwargs):
phone_number = request.data['phone_number']
return self.is_phone_valid(phone_number)
You have to define the phone_number variable. I hope you're using DRF, then the parameter phone_number will be available in request.data
class GetPhoneNumber(CreateAPIView):
queryset = TempRegistration.objects.all()
serializer_class = AccountsSerializer
def is_phone_valid(phone_number):
if phone_number:
MOBILE_REGEX = re.compile('^(?:\+?88)?01[15-9]\d{8}$')
if MOBILE_REGEX.match(phone_number):
return True
else:
return False
else:
return False
def post(self, request, *args, **kwargs):
phone_number = request.data['phone_number']
return Response({'result' : self.is_phone_valid(phone_number)})
If you want to extract the phone number in the POST parameters [wiki], then you should access this in self.request.POST.
Furthermore you can not return a boolean as the result of a request. This needs to be a HttpResponse, for example a JsonResponse object [Django-doc]:
from django.http import JsonResponse
class GetPhoneNumber(CreateAPIView):
queryset = TempRegistration.objects.all()
serializer_class = AccountsSerializer
def is_phone_valid(self, phone_number):
if phone_number:
MOBILE_REGEX = re.compile('^(?:\+?88)?01[15-9]\d{8}$')
if MOBILE_REGEX.match(phone_number):
return True
else:
return False
else:
return False
def post(self, request, *args, **kwargs):
return JsonResponse(
{'result': self.is_phone_valid(self.request.POST.get('phone_number'))}
)

How to update multiple records at once (bulk update) in django API

I need to update categories in many Article in one request.
In ArticleViewSet I have:
def get_serializer_class(self):
if self.action in ['partial_update', 'update']:
return ArticlePostSerializer
return ArticleSerializer
So ArticlePostSerializer need to be changed.
This is my serializers code:
class ArticleShortCategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = 'id', 'name'
class ArticleSerializer(serializers.ModelSerializer):
categories = serializers.SerializerMethodField()
def get_categories(self, obj):
return ArticleShortCategorySerializer(obj.categories, many=True).data
class Meta:
model = Article
read_only_fields = 'id'
fields = ('categories', 'text') + read_only_fields
class ArticlePostSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = 'id', 'categories', 'text'
I tried to add:
class ArticlePostListSerializer(serializers.ListSerializer):
and
class Meta:
list_serializer_class = ArticlePostListSerializer
But it doen't work.
How to change this code to do multiple update.
My json request
{
[id: 90, categories: [10,12,14]],
[id: 93, categories: [10,12,14]],
[id: 95, categories: [10,12,14]]
}
Here is sample of CreateMixins OR UpdateMixins you requested.
======================= VIEW ================================
class OrderCreate(mixins.CreateModelMixin,viewsets.GenericViewSet):
pagination_class = None
def get_queryset(self):
return []
def get_serializer_class(self):
return serializers.OrderSerializer
======================= Serializer =============================
class OrderDetailSerializer(serializers.ModelSerializer):
class Meta:
model = crm_models.OrderDetail
fields = (
'product',
'quantity',
'rate_per_unit',
'order_quantity'
)
class OrderSerializer(serializers.ModelSerializer):
order_details = OrderDetailSerializer(many = True)
class Meta:
model = crm_models.OrderMaster
fields = (
'order',
'invoice_number',
'client',
'beat_check',
'target_customer',
'order_editor_client_employee',
'order_marked',
'order_saved',
'edit_marked',
'edit_saved',
'adhoc',
'order_details'
)
def create(self, validated_data,*args,**kwargs):
ordersdetails_data = validated_data.pop('order_details')
user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
user = request.user
validated_data['client'] = user.client
validated_data['order_editor_client_employee'] = user
validated_data['adhoc'] = validated_data['adhoc'] if 'adhoc' in validated_data else False
orderObj = super(OrderSerializer, self).create(validated_data,*args,**kwargs)
orderdetails = []
for details in ordersdetails_data:
orderdetails.append(crm_models.OrderDetail(
product= details['product'],
quantity = details['quantity'],
rate_per_unit = details['rate_per_unit'],
order_quantity = details['order_quantity'],
order = orderObj
))
crm_models.OrderDetail.objects.bulk_create(orderdetails)
return orderObj
In Update view function name would be changed to update, you can find more documentation http://www.django-rest-framework.org/api-guide/generic-views/#createmodelmixin
I found K. Moe's answer to this question: Django Rest Framework POST Update if existing or create much easier to understand and implement. You only need to add a create method in the serializer and use mixins.CreateModelMixin, generics.GenericAPIView in the view. Then you can use a POST request, instead of PUT/PATCH. It allows to create AND update data stored in your database. My code for the view:
class ZipCodeList(mixins.CreateModelMixin, generics.GenericAPIView):
def post(self, request, format=None):
is_many = isinstance(request.data, list)
if is_many:
serializer = ZipCodeSerializer(data=request.data, many=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)
else:
serializer = ZipCodeSerializer(data=request.data)
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)
#Greg Holst, why so much duplication? Why not:
class ZipCodeList(mixins.CreateModelMixin, generics.GenericAPIView):
def post(self, request, format=None):
serializer = ZipCodeSerializer(data=request.data, many=isinstance(request.data, list))
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)
Anyway, that only worked for me for creating new objects, didn't work to create-or-update in one sweep (it kept telling me these objects already exist), so this is what I did:
class ContributorSyncListAPIView(ListAPIView):
permission_classes = (isSuperUserPermission,)
allowed_methods = ("GET", "PUT")
lookup_field = "airtable_id"
serializer_class = ContributorSyncSerializer # Doesn't get used in Put, just in Get.
model = Contributor
def get_queryset(self):
return self.model.objects.all()
def put(self, request, format=None):
objects = list(request.data)
# objects is a list of OrderedDicts
try:
for o in objects:
try:
instance = self.model.objects.get(
**{self.lookup_field: o.get(self.lookup_field)}
)
for key, value in o.items():
setattr(instance, key, value)
except self.model.DoesNotExist:
instance = self.model(**o)
instance.save()
return Response(objects, status=status.HTTP_200_OK)
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
Note my code above is very light on validation just because it was for a process of syncing models from one environment to another by a superuser, different environments with an identical codebase; so the assumption was the data had already been validated when the data was entered into the first environment. For any other purpose you'd want to validate more. But this is what I had to do to handle a list of objects that may need to be created or updating, on an object-by-object basis.

Update user error with Django API rest framework

I can`t update users because Django gives me this error in postman:
AttributeError at /profesionales/
Got AttributeError when attempting to get a value for field `user` on serializer `ProfesionalesSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'User' object has no attribute 'user'.
Request Method: PUT
Request URL: http://127.0.0.1:8000/profesionales/
Django Version: 1.11.6
Python Executable: C:\Users\Ismael\AppData\Local\Programs\Python\Python36\python.exe
Python Version: 3.6.3
Here is my code:
view.py
#Listar todos los profesionales o crear uno
#profesionales/
class ProfesionalesList(APIView):
def get_object(self, pk):
try:
return User.objects.get(username=pk)
except User.DoesNotExist:
raise Http404
def get(self, request ):
usuarios = Profesionales.objects.all()
usuarioSerializer = ProfesionalesSerializer(usuarios, many=True)
return Response(usuarioSerializer.data)
def post(self, request):
profesionalSerializer = ProfesionalesSerializer(data=request.data)
if profesionalSerializer.is_valid():
profesionalSerializer.save()
return Response(profesionalSerializer.data, status=status.HTTP_201_CREATED)
else:
return Response(profesionalSerializer.errors, status=status.HTTP_400_BAD_REQUEST)
def put(self, request, *args, **kwargs):
instance = self.get_object(request.data.get('user').get('username'))
profesionalSerializer = ProfesionalesSerializer(instance, data=request.data)
if profesionalSerializer.is_valid():
profesionalSerializer.save()
return Response(profesionalSerializer.data, status=status.HTTP_201_CREATED)
else:
return Response(profesionalSerializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
class UserSerializer(serializers.Serializer):
username = serializers.CharField()
first_name = serializers.CharField(allow_blank=True)
last_name = serializers.CharField(allow_blank=True)
email = serializers.CharField(allow_blank=True)
class Meta:
fields = ('username', 'first_name', 'last_name', 'email')
def create(self, validated_data):
user = User.objects.create(**validated_data)
return user
class ProfesionalesSerializer(serializers.Serializer):
user = UserSerializer()
numColegiado = serializers.CharField(allow_blank=False)
class Meta:
fields = ('user', 'numColegiado')
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
profesional = Profesionales.objects.create(user=user, **validated_data)
return profesional
def update(self, instance, validated_data):
num_colegiado = validated_data.get('numColegiado')
user_data = validated_data.pop('user')
user = User.objects.get(**user_data)
profesionales = user.profesionales
if num_colegiado:
profesionales.numColegiado = num_colegiado
profesionales.save()
return instance
model.py
class Profesionales(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dni = models.CharField(max_length=9, blank=True, default='')
numColegiado = models.CharField(max_length=8, blank=True, default='')
valoracionMedia = models.FloatField(blank=True, default=0)
numVotos = models.IntegerField(blank=True, default=0)
def __str__(self):
return self.numColegiado
Ok, I have it - but to be honest, you should rethink your API design.
The problem is that - there is no problem -I mean from yout code I cannot reproduce the error. It is probably I assumed the User model - if you can paste the user model definition would be great (or if this is a standard django user - also mention that).
So firstly I would change the serializers to model serializers:
serializers.py
class UsernameValidator(object):
def set_context(self, serializer_field):
"""
This hook is called by the serializer instance,
prior to the validation call being made.
"""
# Determine the existing instance, if this is an update operation.
self.instance = getattr(serializer_field.parent, 'instance', None)
if not self.instance:
# try to get user from profesionales:
root_instance = getattr(serializer_field.root, 'instance', None)
self.instance = getattr(root_instance, 'user', None)
def __call__(self, value):
if self.instance and User.objects.filter(username=value).exclude(id=self.instance.id).exists():
raise ValidationError('Username already exists.')
if not self.instance and User.objects.filter(username=value).exists():
raise ValidationError('Username already exists.')
class UserSerializer(serializers.ModelSerializer):
username = serializers.CharField(max_length=128, validators=[UsernameValidator()])
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email')
class ProfesionalesSerializer(serializers.ModelSerializer):
user = UserSerializer()
numColegiado = serializers.CharField(allow_blank=False)
class Meta:
model = Profesionales
fields = ('user', 'numColegiado')
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
profesional = Profesionales.objects.create(user=user, **validated_data)
return profesional
def update(self, instance, validated_data):
user_data = validated_data.pop('user')
user = instance.user
userSerializer = UserSerializer(user, data=user_data)
if userSerializer.is_valid(raise_exception=True):
userSerializer.save()
num_colegiado = validated_data.get('numColegiado')
if num_colegiado:
instance.numColegiado = num_colegiado
instance.save()
return instance
As you can note - I've added the UsernameValidator which is pretty important for API to work properly 0- it basically search for existing user instance and check if username exists or not;
I've also changed the update method - now it is using the UserSerializer explicite; Also corrected some bugs - returning validated_data instead of instance and so on.
At the end views.py:
class ProfesionalesList(APIView):
def get_object(self, pk):
try:
return User.objects.get(username=pk)
except User.DoesNotExist:
raise Http404
def get(self, request ):
usuarios = Profesionales.objects.all()
usuarioSerializer = ProfesionalesSerializer(usuarios, many=True)
return Response(usuarioSerializer.data)
def post(self, request):
profesionalSerializer = ProfesionalesSerializer(data=request.data)
if profesionalSerializer.is_valid(raise_exception=True):
profesionalSerializer.save()
return Response(profesionalSerializer.data, status=status.HTTP_201_CREATED)
def put(self, request, *args, **kwargs):
user = self.get_object(request.data.get('user').get('username'))
profesionalSerializer = ProfesionalesSerializer(user.profesionales, data=request.data)
if profesionalSerializer.is_valid(raise_exception=True):
profesionalSerializer.save()
return Response(profesionalSerializer.data, status=status.HTTP_200_OK)
I've shorten the code - using the raise_exception in is_valid method.
Actually - sorry for not following the stackoverflow rules - and do not provide an answer for your actual problem - but I strongly believe that analyzing the example you can figure it out. If you have any more questions - please ask.
On your class ProfesionalesSerializer, you have defined user as an instance of UserSerializer. Instead, user should be a field. You can't use serializers as "fields".
Edit: Ignore this. Turns out you can. See here: http://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects
(thanks to #opalczynski)

confusion on which serializer to use to update user profile

I have a profile model for user to update. I want to have a rest api for that. I have a profile model which is too big than usual. I am working to update the user profile but I am having confusion on which serializer should i use to update the profile. Here is what i have done till now
I created the serializer for user and user profile.
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('token', 'user','current_location', 'permanent_location', 'dob',
'about_me', 'gender_status', 'create_profile_for', 'marital_status',
'height', 'weight', 'body_type', 'complexion',)
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer(required=True)
class Meta:
model = User
fields = ('id', 'profile', 'username', 'email', 'first_name', 'last_name',)
def update(self, instance, validated_data):
# instance = super(UserSerializer, self).update(validated_data)
profile_data = validated_data.pop('profile')
#updating user data
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.email = validated_data.get('first_name', instance.email)
#updating profile data
if not instance.profile:
Profile.objects.create(user=instance, **profile_data)
instance.profile.current_location = profile_data.get('current_location', instance.profile.current_location)
instance.profile.permanent_location = profile_data.get('permanent_location', instance.profile.permanent_location)
instance.profile.weight = profile_data.get('weight', instance.profile.weight)
instance.profile.height = profile_data.get('height', instance.profile.height)
instance.profile.about_me = profile_data.get('about_me', instance.profile.about_me)
instance.profile.create_profile_for = profile_data.get('create_profile_for', instance.profile.create_profile_for)
instance.profile.body_type = profile_data.get('body_type', instance.profile.body_type)
instance.save()
return instance
This is my view
class UserProfile(APIView):
serializer_class = UserSerializer
def get(self, request, token=None, format=None):
"""
Returns a list of profile of user
"""
reply={}
try:
profile_instance = Profile.objects.filter(user=self.request.user)
if token:
profile = profile_instance.get(token=token)
reply['data'] = self.serializer_class(profile).data
else:
reply['data'] = self.serializer_class(profile_instance, many=True).data
except:
reply['data']=[]
return Response(reply, status.HTTP_200_OK)
def post(self, request, token=None, format=None):
"""
update a profile
"""
profile=None
if not token is None:
try:
profile = Profile.objects.get(user=request.user, token=token)
except Profile.DoesNotExist:
return error.RequestedResourceNotFound().as_response()
except:
return error.UnknownError().as_response()
serialized_data = self.serializer_class(profile, data=request.data, partial=True)
reply={}
if not serialized_data.is_valid():
return error.ValidationError(serialized_data.errors).as_response()
else:
profile = serialized_data.save(user=request.user)
reply['data']=self.serializer_class(profile, many=False).data
return Response(reply, status.HTTP_200_OK)
How should i handle my user profile update?
Updated my UserSerializer with update function
UPDATE with Dean Christian Armada solution
class UserProfile(APIView):
serializer_class = ProfileSerializer
def get(self, request, token=None, format=None):
"""
Returns a list of profile of user
"""
reply={}
try:
profile_instance = Profile.objects.filter(user=self.request.user)
if token:
profile = profile_instance.get(token=token)
reply['data'] = self.serializer_class(profile).data
else:
reply['data'] = self.serializer_class(profile_instance, many=True).data
except:
reply['data']=[]
return Response(reply, status.HTTP_200_OK)
def put(self, request, token=None, *args, **kwargs):
"""
update a profile
"""
print('token', token)
if token:
try:
profile = Profile.objects.get(token=token)
except:
return Response(status=status.HTTP_400_BAD_REQUEST)
serialized_data = self.serializer_class(profile, data=request.data)
reply={}
if serialized_data.is_valid():
profile = serialized_data.save(user=request.user)
reply['data'] = self.serializer_class(profile, many=False).data
return Response(reply, status.HTTP_200_OK)
else:
return Response(serialized_data.errors, status.HTTP_400_BAD_REQUEST)
With your code, i get This QueryDict instance is immutable error
It's a standard to use the PUT request method for updating in your current code and situation it is better to use the ProfileSerializer since it has more fields. Also, the way I see it you only have three fields in the User object that can be changed and those three fields I assume is rarely changed.
views.py
def put(self, request, *args, **kwargs):
"""
update a profile
"""
data = request.data
print data
try:
profile = Profile.objects.get(token=data.get('token'))
except:
return Response(status=status.HTTP_400_BAD_REQUEST)
serializer = self.serializer_class(profile, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status.HTTP_200_OK)
else:
return Response(serializer.errors,
status.HTTP_400_BAD_REQUEST)
serializers.py
class ProfileSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Profile
fields = ('user', 'height', 'weight', 'token')
def to_internal_value(self, data):
first_name = data.pop('first_name', None)
last_name = data.pop('last_name', None)
data = super(ProfileSerializer, self).to_internal_value(data)
data['first_name'] = first_name
data['last_name'] = last_name
return data
def update(self, instance, validated_data):
first_name = validated_data.pop('first_name', None)
last_name = validated_data.pop('last_name', None)
user_inst_fields = {}
if first_name:
user_inst_fields['first_name'] = first_name
if last_name:
user_inst_fields['last_name'] = last_name
if user_inst_fields:
User.objects.update_or_create(pk=instance.user.id,
defaults=user_inst_fields)
profile, created = Profile.objects.update_or_create(
token=instance.token, defaults=validated_data)
return profile
Simply just set the user to read only for it to avoid validation

Categories