not a valid choice in django restframework serializer - django-rest-framework

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

Related

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 }
}

Cannot generate post request for multiple data

I am trying to take input multiple data object in post request, but getting such error.
non_field_errors: [ Invalid data. Expected a dictionary, but got a list. ]
models.py
class OrderProduct(BaseModel):
product = models.ForeignKey(Product,on_delete=models.CASCADE)
order = models.ForeignKey(Order,on_delete=models.CASCADE)
order_product_price = models.FloatField(blank=False,null=False,default=0) # product may belong to offer do the price
order_product_qty = models.FloatField(default=1)
serializers.py
class OrderProductSerializer(serializers.ModelSerializer):
def update(self,instance,validated_data):
product = self.validated_data.pop('product')
order = self.validated_data.pop('order')
instance.orderproduct_qty =
self.validated_data.get('orderproduct_qty',instance.orderproduct_qty)
instance.product = product
instance.order = order
instance.save()
return instance
class Meta:
model = OrderProduct
fields = '__all__'
views.py
def post(self,request,*args,**kwargs):
if request.data['contact_number'] == '':
request.POST._mutable =True
request.data['contact_number'] = request.user.mobile_number
request.POST._mutable = False
serializer = OrderSerializer(data=request.data,many=isinstance(request.data,list),context={'request': request})
print(serializer)
if serializer.is_valid():
serializer.save(user = request.user,created_by = request.user)
return Response(serializer.data,status=status.HTTP_200_OK)
else:
return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)
urls.py
path('orderproduct/',views.OrderProductList.as_view()),
When you call serializer.save(). It's only perform create() action which is only create one and accept dictionary data type only. If you want to save multiple data like that, you will have to override the create function of the serializer class. You can do something similar like this or run a for loop.
serializers.py
def create(self, validate_data):
# Get the data objects you need to perform bulk create
order_products = OrderProduct.objects.bulk_create(validate_data)
return order_products
views.py
if serializer.is_valid(raise_exception=True):
# Replace the serializer.save() by this line to trigger the create method in serializer
self.perform_create(serializer)
return Response(...)

How to return a queryset as a JSON response in Django RESTFramework?

I am trying to return a queryset as a JSON response in DRF but I always get a TypeError Object Not JSON serializable.
I have tried different methods but none of them worked. I have tried to use the JSONRenderer class and I have also tried to serialize a single object using SentSerializer. But nothing seems to solve the issue. I have just started learning DRF so it is a little confusing to me and really don't understand how serializers work and I am not sure if I have written them correctly or using them correctly.
# models
...
from django.contrib.auth.models import User
class Sentence(models.Model):
sent = models.CharField(max_length=255)
sent_correct = models.CharField(max_length=255, null=True)
pub_date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='sentences')
# serializers
...
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
username = serializers.CharField(max_length=50)
class Meta:
model = User
# Tuple of serialized model fields (see link [2])
fields = ( "id", "username", "password", )
class SentSerializer(serializers.ModelSerializer):
sent = serializers.CharField(max_length=255)
sent_correct = serializers.CharField(max_length=255, required=False)
author = UserSerializer(read_only=True, required=False)
class Meta:
model = Sentence
fields = (
'sent', 'sent_correct', 'author'
)
# views
...
class SentCreateAPIView(APIView):
serializer_class = SentSerializer
permission_classes = (IsAuthenticated,)
def post(self, request):
ss = Sentence.objects.filter(author=request.user)[0:1]
ss = list(ss)
print("sentences " + str(ss))
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
vd = serializer.validated_data
sent_str = vd['sent']
s = Sentence(sent=sent_str, sent_correct=sent_str)
s.author = request.user
print(vd)
print(request.user)
s.save()
sent = nlp(sent_str)
tokens = [t.text for t in sent] # this list returns successfully
return Response(
{ 'sent': sent_str,
'sent_correct': sent_str,
'tokens': tokens,
'ss': ss, # this list throws TypeError},
status=status.HTTP_201_CREATED
)
Jakub Maślanka answered my question on Facebook in Django Python Web Framework group. I had to serialize the queryset like this:
# views.py
...
return Response(
{ 'sent': sent_str,
'sent_correct': sent_str,
'tokens': tokens,
'ss': SentSerializer(ss, many=True).data},
status=status.HTTP_201_CREATED
)
ss -> dict
Why dont you use generics.ListAPIView.
It has get_query_set method
You just need to override that method by your own query set
Less code is better -> class based and framework

How to make QueryDict instance is mutable in django

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.

django rest_framework serializer parameter

I am using django rest_framework to provide jsGrid json data.
As the rest_framwork example, I create a object fit the jsGrid format
class jsGridResp(object):
def __init__(self, data, itemsCount):
self.data = data
self.itemsCount = itemsCount
and the class based view, create a get function
class RateListViewSet(mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
"""
API endpoint that allows user to be viewed or edited
"""
queryset = RateList.objects.all().order_by('-create_date')
serializer_class = RateListSerializer
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
obj = queryset.get(pk=self.request.POST["id"])
self.check_object_permissions(self.request, obj)
return obj
def get(self, request, format=None):
pageIndex = request.GET.get('pageIndex')
pageSize = request.GET.get('pageSize')
sortField = request.GET.get('sortField', 'id')
sortOrder = request.GET.get('sortOrder', 'asc')
sortOrder = "" if sortOrder == "asc" else "-"
rows = RateList.objects.all().order_by("{}{}".format(sortOrder, sortField))
itemsCount = rows.count()
paginator = Paginator(rows, pageSize)
try:
rows = paginator.page(pageIndex)
except PageNotAnInteger:
rows = paginator.page(1)
except EmptyPage:
rows = paginator.page(paginator.num_pages)
result = jsGridResp(data=rows, itemsCount=itemsCount)
serializer = RateListGetSerializer(result)
json = JSONRenderer().render(serializer.data)
return Response(json)
then I create two serializer to serialize the data
class RateListSerializer(serializers.ModelSerializer):
class Meta:
model = RateList
fields = ('id', 'rate_code', 'hr01', 'hr02', 'hr03', 'hr04', 'hr05', 'hr06',
'hr07', 'hr08', 'hr09', 'hr10', 'hr11', 'hr12', 'hr13', 'hr14',
'hr15', 'note', 'create_date', 'update_date')
read_only_fields = ('create_date', 'update_date')
def update(self, instance, validated_data):
result = instance.update(id=instance.id, **validated_data)
return result
def destroy(self, instace, validated_data):
return "{seccuess: true}"
class RateListGetSerializer(serializers.Serializer):
itemsCount = serializers.IntegerField()
data = RateListSerializer(many=True)
but I have many model need to do like these.
can I use just one serializer to serialize all model.
I want to create a jsGridGetSerializer can pass in a model parameter, so I don't need to create many simple serialzer to do the same thing.
Is this possible?
If I understand your problem correctly, you want a generic serializer which accepts a Model variable as a parameter in its Meta class.
One way to do this is to pass in the model name as a url keyword argument, then catching it in views.py. Then you can override the Meta model via overriding the get_serializer_class:
serializers.py
class GenericSerializer(serializers.ModelSerializer):
class Meta:
model = None
views.py
class GenericViewSet(viewsets.ModelViewSet):
def get_queryset(self):
model = self.kwargs.get('model')
return model.objects.all()
def get_serializer_class(self):
GenericSerializer.Meta.model = self.kwargs.get('model')
return GenericSerializer

Resources