Getting "AttributeError: 'QuerySet' object has no attribute '_meta'" on django rest "PUT" method - django-rest-framework

I am trying to update the record with PUT method, Getting AttributeError: 'QuerySet' object has no attribute '_meta'.
My models.py:
class TableInfo(models.Model):
table_name = models.CharField(max_length=10)
columns = JSONField(null=False)
serializer.py:
class TableInfoSerializer(serializers.ModelSerializer):
class Meta:
model = TableInfo
fields = '__all__'
views.py :
#api_view(['GET','PUT'])
def table_info(request):
try:
queryset = TableInfo.objects.all()
print("1")
except TableInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
print("2")
serializer_class = TableInfoSerializer(queryset, many=True)
return Response(serializer_class.data)
elif request.method == 'PUT':
print(request.data)
serializer = TableInfoSerializer(queryset, data=request.data)
if serializer.is_valid():
serializer.save()
print("4")
return HttpResponse(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
code is breaking at if serializer.is_valid():
On "GET" I am getting the result. Please help me with "PUT" method.

This error happens with PUT because the serializer tries to access the Meta class on the model instance it is updating, but fails because you are not passing a model instance - you're passing a queryset as indicated in the comments.
So you need to pass an instance, and to specify which instance you would normally pass the instance id via the URL. For that you would be best to separate out your views, and create a table_detail view for retrieving and updating a specific instance.
#api_view(['GET','PUT'])
def table_detail(request, pk):
try:
table_info = TableInfo.objects.get(pk=pk) # Lookup a specific object
except TableInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer_class = TableInfoSerializer(table_info)
return Response(serializer_class.data)
elif request.method == 'PUT':
serializer = TableInfoSerializer(table_info, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Your table_info view can then just handle the list operation.
#api_view(['GET'])
def table_info(request):
if request.method == 'GET':
queryset = TableInfo.objects.all()
serializer_class = TableInfoSerializer(queryset, many=True)
return Response(serializer_class.data)

Related

How to write the functionality in generics.ListAPIView which can be written in APIView in Django DRF

I have a function base view which get 2 parameters from URL
http://127.0.0.1:8000/api/v1/contest/0b36d92a-51a7-4752-9df1-e5f2733116c1/paintings/
#api_view(['GET',])
#permission_classes([AllowAny])
def Contest_detail_by_id_and_category(request, id, category_name):
if request.method == 'GET':
artcontests = Artwork.objects.filter(artcontest = id,category__name__iexact=category_name)
serializer = ArtworkSerializer(artcontests, many=True)
# serializer = ArtworkSerializer(artcontests, many=True)
return Response(serializer.data)
which give proper result , but when I try to write the same functionality in generics.ListAPIView it gives
TypeError at /api/v1/contesty/0b36d92a-51a7-4752-9df1-e5f2733116c1/paintings/
object of type 'method' has no len()
class Contest_detail_by_id_category(generics.ListAPIView):
serializer_class = ArtworkSerializer1(many=True)
permission_classes = [AllowAny]
def queryset(self):
queryset = Artwork.objects.filter(artcontest = self.kwargs['id'],category__name__iexact=self.kwargs['category_name'])
# queryset = self.get_queryset()
serializer = ArtworkSerializer1(queryset)
return Response(serializer.data)
Can anyone help me - how to write the correct view in generics.ListAPIView or viewsets.ModelViewSet
Remove many=True from serializer_class. Create get_queryset function as below, instead of your queryset function.
class Contest_detail_by_id_category(generics.ListAPIView):
serializer_class = ArtworkSerializer1
queryset = Artwork.objects.all()
permission_classes = [AllowAny]
def get_queryset(self):
return self.queryset.filter(artcontest = self.kwargs['id'],category__name__iexact=self.kwargs['category_name'])
Update: Working:
ListAPIView class inherits ListModelMixin.
class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
See here. https://github.com/encode/django-rest-framework/blob/master/rest_framework/mixins.py

Retrieve Single Object in Modelviewset

I was using CBV, ModelViewSet but somehow my API Keep showing all the Objects inside, how i get my ModelViewSet retrieve a single object that base on my sku ?
api/views.py
class SamAPIViewSet(viewsets.ModelViewSet):
queryset = SamModels.objects.all()
serializer_class = SamSerializers
lookup_field = 'pk'
def get_queryset(self):
return self.queryset
def get_object(self):
sku_id = self.kwargs['pk']
return self.get_queryset().filter(id=sku_id)
def list(self, request):
products = SamModels.objects.all()
serializers = self.get_serializer(products, many=True)
return Response(serializers.data)
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializers = self.get_serializer(instance)
return Response(serializers.data)
api/urls.py
sam_api_list = SamAPIViewSet.as_view({
'get': 'list'
})
sam_api_detail = SamAPIViewSet.as_view({
'get': 'retrieve'
})
urlpatterns = [
url(r'sam', sam_api_list, name='api-sam'),
url(r'sam/<int:pk>', sam_api_detail, name='api-sam-detail'),
]
sam/1, it return all of my objects, i am not sure what wrong, my object has name, id, sku and how do i change my view to get retrieve single object base on my sku and not my id ?
Your routes are upside down. Try this:
urlpatterns = [
url(r'sam/<int:pk>', sam_api_detail, name='api-sam-detail'),
url(r'sam', sam_api_list, name='api-sam'),
]
Django will select the first expression that matches. If r'sam' is above r'sam/int:pk' then it will select r'sam' without going down to the next entry
url(r'sam/<int:pk>', sam_api_detail, name='api-sam-detail'),
url(r'sam', sam_api_list, name='api-sam'),
to
path(r'sam/<str:pk>', sam_api_detail, name='api-sam-detail'),
path(r'sam', sam_api_list, name='api-sam'),
remove
def get_queryset(self):
return self.queryset
def get_object(self):
sku_id = self.kwargs['pk']
return self.get_queryset().filter(id=sku_id)
and change lookup_field = 'pk' to lookup_field = 'sku'

Original exception text was: 'QuerySet' object has no attribute 'weight'

I got an Exception
Got AttributeError when attempting to get a value for field weight on serializer WeightHistorySerializer.
The serializer field might be named incorrectly and not match any attribute or key on the QuerySet instance.
Original exception text was: 'QuerySet' object has no attribute 'weight'.
When I tried to retrive data.
models.py
class WeightHistory(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
weight = models.FloatField(null=False, blank=False)
created_at = models.DateTimeField(auto_now_add=True)
serializers.py
class WeightHistorySerializer(serializers. HyperlinkedModelSerializer):
class Meta:
model = WeightHistory
fields = (
'id',
'weight',
'user_id',
'created_at'
)
read_only_fields = ('id',)
views.py
def weight_history_detail(request, user_id):
# Retrieve, update or delete a weight_history/detail.
try:
weight_history = WeightHistory.objects.filter(user_id=user_id)
except WeightHistory.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = WeightHistorySerializer(weight_history)
return Response(serializer.data)
If it change to
weight_history = WeightHistory.objects.get(user_id=user_id)
It returns only one row, But I want all the rows with given user_id. So, What should I do to get all list with given user_id.
'QuerySet' object has no attribute 'weight'.
Yes. QuerySet is a Set, a list of objects.
<QuerySet [<Object1>, <Object2>,..]>
And that list has no attribute weight. Instead, the objects inside the QuerySet has the attribute weight.
weight_history = WeightHistory.objects.filter(user_id=user_id)
filter returns a QuerySet, a list of WeightHistory objects with user_id=user_id.
And you are trying to serialize the list as a single object.
Instead of this:
serializer = WeightHistorySerializer(weight_history)
Do this:
serializer = WeightHistorySerializer(weight_history, many=True)
many=True tells the serializer that a list of objects being passed for serialization.
Moreover,
try:
weight_history = WeightHistory.objects.filter(user_id=user_id)
except WeightHistory.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
This doesn't throw an exception at all. filter returns an empty QuerySet if no objects exist. <QuerySet []>.
So the final code is:
def weight_history_detail(request, user_id):
# Retrieve, update or delete a weight_history/detail.
weight_history = WeightHistory.objects.filter(user_id=user_id)
if weight_history.count()<1:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = WeightHistorySerializer(weight_history, many=True)
return Response(serializer.data)
views.py
def weight_history_detail(request, user_id):
# Retrieve, update or delete a weight_history/detail.
try:
weight_history = WeightHistory.objects.get(user_id=user_id) #get
except WeightHistory.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = WeightHistorySerializer(weight_history)
return Response(serializer.data)
use get instead of filter it will solve you error

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

how to update serializer.validated_data in DRF

good day
me need update validated_data dict
example
class ProductsApiView(APIView):
permission_classes = [AllowAny]
def post(self, request):
serializer = ProductSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
product = serializer.save()
print(serializer.validated_data)
return Response({'message': 'created'}, status=status.HTTP_201_CREATED)
class ProductSerializer(serializers.ModelSerializer):
def create(self, validated_data):
validated_data['my_data'] = 'my_new_data'
return super(ProductSerializer, self).create(validated_data)
class Meta:
model = Product
fields = ['id', 'title']
when i make print(serializer.validated_data)
OrderedDict([('title', 'testttt')])
why in dict no my data "my_data" ?
You can provide arbitrary additional context by passing a context argument when instantiating the serializer. For example:
serializer = AccountSerializer(account, context={'request': request})
serializer.data
The context dictionary can be used within any serializer field logic, such as a custom .to_representation() method, by accessing the self.context attribute.
Reference

Resources