How to make QueryDict instance is mutable in django - django-rest-framework

I have a serialized classs like this:
class EmployeeSerializer(serializers.ModelSerializer):
# TODO: Define serializer fields here
bio = BioSerializer()
designation = GroupListSerializer()
department = GroupListSerializer()
#user = UserSerializer()
class Meta:
model = Employee
fields = '__all__'
# fields = ['user','tax_id_number','account_number','joining_date','designation','department','gender','marital_status','id_type','birthday','ethnicity','preferred_language','phone_number','em_contact','address']
def create(self,validated_data):
bio = validated_data.pop('bio')
designation = validated_data.pop('designation')
department = validated_data.pop('department')
new_bio = Bio.objects.create(**bio)
new_designation = Groups.objects(**designation)
new_department = Groups.objects(**department)
employee = list(Employee.objects.create(designation=new_designation,department=new_department,
bio=new_bio,**validated_data))
employee = json.dumps(employee)
return employee
And my views are as follows:
class EmployeeRecordView(generics.CreateAPIView):
queryset=Employee.objects.all()
serializer_class=EmployeeSerializer
def post(self, request):
serializer = EmployeeSerializer(data=request.data)
if serializer.is_valid(raise_exception=ValueError):
serializer.create(validated_data=request.data)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.error_messages,
status=status.HTTP_400_BAD_REQUEST)
The problem is when I try to make a 'POST' request I get the following logs below:
Exception Type: AttributeError
Exception Value:
This QueryDict instance is immutable
How do I make POSTing successfull. Where am I going wrong with this approach I have implemented above?

You shouldn't have to call serializer.create yourself - I believe the line that is throwing the error is serializer.create(validated_data=request.data). You can try this:
...
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
Another thing is when you set raise_exception=True, it already throws an error response so you don't need to put it in an if/else block.

Related

not a valid choice in django restframework serializer

i set my post model with choice and migrate model.
after that i create postserializer for create
and i run the server and post with data in postman for test,
but i got the 'is not a valid choice' err on serializer.
here is my model,
class Post(models.Model):
CATEGORY_CHOICES = (
('mc', 'MIRACLE'),
('hw', 'HOMEWORK')
)
title = models.CharField(max_length=50)
category = models.CharField(max_length=2, choices=CATEGORY_CHOICES)
content = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
serializer,
class PostCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
view
class PostAPI(APIView):
def post(self, request):
data = {
'title' :request.data['title'],
'category':request.data['category'],
'content' :request.data['content'],
'author' :request.user.id
}
serializer = PostCreateSerializer(data=data)
if serializer.is_valid():
serializer.save()
else:
print(serializer.erros)
...
and request data (in Postman)
{
'title':'test_title',
'category':'HOMEWORK',
'content':'test_content'
}
result is
{'category': [ErrorDetail(string='"HOMEWORK" is not a valid choice.', code='invalid_choice')]}
i tried request with changing category 'HOMEWORK' to 'hw'
then it works
but i want request with large one
Your error on serialize level. Try this
readable_to_choice = {"HOMEWORK": "hw", "MIRACLE": "mc"}
data = {'title' :request.data['title'],
'category': readable_to_choice.get(request.data['category']),
'content' :request.data['content'],
'author' :request.user.id}
serializer = PostCreateSerializer(data=data)
UPD: you should make your api func like this:
serializer = PostCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
print(serializer.data) # do logic with that data
and try to override to_internal_value method in your serializer. for example:
def to_internal_value(self, data):
readable_to_choice = {"HOMEWORK": "hw", "MIRACLE": "mc"}
data["category"] = readable_to_choice.get(data["category"])
res = super().to_internal_value(data)
return res
I think this way is more accurate

I have to create serializer instance without data since i am not accepting any data from user

Here i am trying to create serializer instance without data argument because all i want to create a "Like" object which requires "user" object which i can get from request and "post" object that i am getting through querying Post model with pk but since i am not passing any data argument while deserializing it will throw an error.
So how can create instance without passing data argument or do i have to change my code and add data argument?
class Like(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="likes")
liked_by = models.ForeignKey(User, on_delete=models.RESTRICT, related_name = "liked_posts")
def __str__(self):
return "{0} liked by {1}".format(self.post.img, self.liked_by.username)
class LikeSerializer(serializers.ModelSerializer):
liked_by = UserSerializer(read_only=True)
class Meta:
model = Like
fields = ("id","post", "liked_by")
class AddLike(APIView):
permission_classes = [IsAuthenticated]
def post(self, request, pk):
post = Post.objects.get(pk=pk)
user = request.user
serializer = LikeSerializer()
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializer.save(post=post, liked_by=user)
return Response(data= serializer.data, status=status.HTTP_201_CREATED)
I think you can set the post field as read_only in the LikeSerializer.
class LikeSerializer(serializers.ModelSerializer):
liked_by = UserSerializer(read_only=True)
class Meta:
model = Like
fields = ("id","post", "liked_by")
extra_kwargs = {
'post': { 'read_only': True }
}

'QuerySet' object has no attribute 'pk'

#models
class Student(models.Model):
firstname = models.CharField(max_length=100,default='ll')
lastname = models.CharField(max_length=100,default='fewf')
id_code = models.CharField(max_length=10,default=0,unique=True)
melli = models.CharField(max_length=30,default=0,unique=True)
personal_pic = models.ImageField(upload_to=studentFile)
major = models.ForeignKey(Major, on_delete=models.PROTECT,default=0)
date_of_start = models.DateField(default=datetime.date.today)
def __str__(self):
return self.id_code
#views
class loginView(APIView):
def post(self, request):
data = request.data
melli = data.get('melli')
id_code = data.get('id_code')
student = Student.objects.filter(id_code=id_code,melli=melli)
if not student.exists():
return Response('error')
serializer = StudentSerializer(student,data=data)
serializer.is_valid()
return Response(serializer.data)
when i try to submit a post request i was excepted to recive a response but i got an error. how can i solve it?
You serialize a collection of elements, so you should work with many=True:
serializer = StudentSerializer(student,data=data, many=True)
in case you want to only work with a single object, you need to retrieve a single object, not a collection of objects, for example with .get(…) [Django-doc] instead of .filter(…) [Django-doc].
Model.filter returns a queryset, which is like a list of models, rather than a specific model. Instead, you should use get, which returns a single model instance:
student = Student.objects.get(id_code=id_code,melli=melli)
If you intend on there being more than 1 student in this specific query, you can add many=True to your serializer instead:
serializer = StudentSerializer(student,data=data, many=True)

DRF: How to get Users object in a get method as a Response?

How can I get all user objects in get method in views.py with other models?
views.py
class WebDashboardViews(generics.ListAPIView):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAdminUser,permissions.IsAuthenticated,)
serializer_class = serializers.DashboardSerializer
def get(self, request, format=None):
all_user_queryset = models.User.objects.all()
if not all_user_queryset:
total_user = 0
else:
total_user = all_user_queryset.count()
total_android_user = models.UserMobileDevice.objects.filter(os="Android")
if not total_android_user:
total_android_count = 0
else:
total_android_count = total_android_user.count()
content = {'total_user':total_user,'total_android_count':total_android_count, all_users: all_user_queryset}
return Response(content)
I want the user object in all_users key. But I am getting an error:
Object of type User is not JSON serializable
My serializer code is :
class DashboardSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
field = ['id','created_at','name']
I am not sure what I am doing here. Any help will be highly appreciated. Thanks
You get that error because you are trying to respond with not-serialized objects here: all_users: all_user_queryset
You need to serialize them first (I guess you have an UserSerializer, if don't, just rename your DashboardSerializer to UserSerializer because it is just what it is):
user_serializer = UserSerializer(all_user_queryset, many=True)
content = {'total_user':total_user,'total_android_count':total_android_count, all_users: user_serializer.data}
return Response(content)

How can one return 400 status code with DRF APIView get method and ModelSerializer?

I have the following code (for the endpoint /things/{id}/permission-to-do/
views.py
class PermissionsToDo(APIView):
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request,*args,**kwargs):
thing_id = kwargs.get('pk')
thing = Thing.objects.filter(pk=thing_id,is_active=True)
serializer = serializers.GetDoPermissionSerializer(thing[0],context={'request': request})
return Response(serializer.data, status=status.HTTP_200_OK)
serializers.py
class serializers.GetDoPermissionSerializer(serializers.ModelSerializer):
def _can_do(self, thing):
return thing.can_be_done_by(self.context['request'].user)
can_do = serializers.SerializerMethodField('_can_do')
class Meta:
model = Thing.objects.filter
fields = ('can_do',)
extra_kwargs = {
'can_do': {'read_only': True},
}
The thing.can_be_done_by(user) method returns a Boolean. This works fine with a correct request but I want to add a way to validate the request and send appropriate status code for client errors, such as status.HTTP_400_BAD_REQUEST
My idea was to just add in views.py a:
if serializer.is_valid():
return Response(serializer.data, status=status.HTTP_200_OK)
else:
return Response(SOMETHING?,status.HTTP_400_BAD_REQUEST)
if I evaluate serializer.is_valid() I obtain an error message saying:
'Cannot call `.is_valid()` as no `data=` keyword argument was '
AssertionError: Cannot call `.is_valid()` as no `data=` keyword argument was passed when instantiating the serializer instance.
if I change the line:
serializer = serializers.GetDoPermissionSerializer(thing[0],context={'request': request})
into:
serializer = serializers.GetDoPermissionSerializer(data=thing[0],context={'request': request})
but then, I get an error suggesting the I should be passing a dictionary as data and not an object. but then I'm not sure how to implement the validate method and how to change the _can_do method to get it to work.
Any idea?
Thanks for your time if you have some to spare!
.validate() method takes a single argument, which is a dictionary of field values. It should raise a ValidationError if necessary, or just return the validated values.
One of the ways you can achieve this is to convert the model object to a dict. So try the following snippet,
class PermissionsToDo(APIView):
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request, *args, **kwargs):
thing_id = kwargs.get('pk')
thing = Thing.objects.filter(pk=thing_id, is_active=True)
serializer = serializers.GetDoPermissionSerializer(data=thing[0].__dict__, context={'request': request}) # Change is here <<<<
if serializer.is_valid():
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(SOMETHING?, status.HTTP_400_BAD_REQUEST)
My suggestion
If you are serializing the object from DB, most of the time it won't raise any validation error. I would suggest that, try to show error message if thing object become a empty queryset.So,
class PermissionsToDo(APIView):
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request, *args, **kwargs):
thing_id = kwargs.get('pk')
try:
serializer = serializers.GetDoPermissionSerializer(Thing.objects.get(id=thing_id), context={'request': request})
return Response(serializer.data, status=status.HTTP_200_OK)
except Thing.DoesNotExist:
return Response(data="object not found", status.HTTP_400_BAD_REQUEST)
I used your "suggestion" to the exception that I had to modify the last line. I replaced:
return Response(data="object not found", status.HTTP_400_BAD_REQUEST)
by :
return Response("object not found", status.HTTP_400_BAD_REQUEST)
or I get the following error:
SyntaxError: positional argument follows keyword argument

Resources