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

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

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

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)

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