Writing a Get Function Gives me an Attribute Error - python

This is my models.py
class Grade(models.Model):
grade = models.CharField(max_length=255, primary_key=True)
This is my views to perform get(post is not required, I can run if post methood is required as well).
class GetGrade(generics. GenericAPIView):
'''
GET check/
'''
queryset = Grade.objects.all()
serializer_class = DataSerializer
def get(self, request, *args, **kwargs):
a_grade = Grade.objects.all()
return Response(
data=DataSerializer(a_grade).data,
status=status.HTTP_200
)
My serializer is below:
class DataSerializer(serializers.ModelSerializer):
class Meta:
model = Grade
fields = ("grade",)
Everything seems straightforward. It might be something silly that I might be doing.
AttributeError at /check/
Got AttributeError when attempting to get a value for field `grade` on serializer `DataSerializer`.
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 'grade'.
Request Method: GET
Request URL: http://127.0.0.1:8000/check/
Django Version: 2.1.5
Exception Type: AttributeError
Exception Value: Got AttributeError when attempting to get a value for field `grade` on serializer `DataSerializer`.
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 'grade'.

How about like this:
class GetGrade(generics.GenericAPIView):
def get(self, request, *args, **kwargs):
a_grade = Grade.objects.all()
return Response(
data=DataSerializer(a_grade, many=True).data, # passed many=True as known argument
status=status.HTTP_200
)
The error occured because you are passing a Queryset through DataSerializer. When you called data method of DataSerializer, it was trying to get value grade attribute from Queryset. That is why, you need to pass many=True, so that, serializer knows you are passing queryset or a list a objects. If you want the serializer to process a single object, then you can do it like this:
class GetGrade(generics.GenericAPIView):
def get(self, request, *args, **kwargs):
a_grade = Grade.objects.all().first() # it will return first object of queryset
return Response(
data=DataSerializer(a_grade).data,
status=status.HTTP_200
)
Finally, a cleaner approach is to use ListModelMixin. For example:
from rest_framework import mixins, generics
class GetGrade(mixins.ListModelMixin, generics.GenericAPIView):
queryset = Grade.objects.all()
serializer_class = DataSerializer
# thats it, no more code needed

In your Serailizers.py you need to use model method to get the objects from your model in API:
class GetGrade(generics. GenericAPIView):
'''
GET check/
'''
model = Grade # <---Add This in place queryset
serializer_class = DataSerializer

Related

Django ViewSet serializer_class is being ignored

I have two models: ModelA and ModelB, with their corresponding serializers ModelASerializer and ModelBSerializer
In a specific viewset, called MyViewSet i have the follwing structure:
class MyViewSetRoot(viewsets.ModelViewSet):
http_method_names = ["get"]
# The returned values are of type "ModelA", so I need it to use that serializer
serializer_class = ModelASerializer
queryset = ""
Finally, in my actual view, I do something like this:
class MyViewSet(MyViewSetRoot):
get(self, request: HttpRequest, *args, **kwargs) -> Response:
ModelA_queryset = ModelA.objects.all()
return Response(
data=ModelA_queryset,
status=status.HTTP_200_OK,
)
I would expect in that case for the queryset to be serialized using the ModelASerializer that I specified in the serializer_class field. However, I get the error
Object of type ModelA is not JSON serializable
If I do this instead:
class MyViewSet(MyViewSetRoot):
get(self, request: HttpRequest, *args, **kwargs) -> Response:
ModelA_queryset = ModelA.objects.all()
serialized_queryset = ModelASerializer(ModelA_queryset, many=True)
return Response(
data=serialized_queryset.data,
status=status.HTTP_200_OK,
)
It works just fine, but I want to avoid serializing explicitly in the view.
Any ideas on what could be actually going on? Am I forced to serialize explicitly in this case?
I think you don't need to customize the get function. In ModelViewSet, the function for the GET API, is list or retrieve. But you don't need to redefine it.
class MyViewSetRoot(viewsets.ModelViewSet):
http_method_names = ["get"]
serializer_class = ModelASerializer
queryset = ModelA.objects.all()
class MyViewSet(MyViewSetRoot):
pass

I receive typerror when use request method in drf

With some help, I solved this issue.
My api is work, but today I found this error when I try to access '/api/v1/docs'
AttributeError at /api/v1/docs/
'NoneType' object has no attribute 'method'
I know that the error is here:
def get_fields(self):
fields = super().get_fields()
if self.context['request'].method in ['POST', 'PATCH', 'PUT']:
fields['products'] = serializers.ListField(
write_only=True,
child=serializers.IntegerField()
)
return fields
When I remove .method, the access to the /api/v1/docs/ works, but my solution to post some products in bundleproducts, doesn't work.
My code:
view.py
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
model = Product
class BundleProductViewSet(viewsets.ModelViewSet):
queryset = BundleProduct.objects.all()
serializer_class = BundleProductSerializer
model = BundleProduct
class BundleViewSet(viewsets.ModelViewSet):
queryset = Bundle.objects.all()
serializer_class = BundleSerializer
model = Bundle
This is probably caused by this serializer being used as a nested serializer in another serializer. So lets say the definition for the serializer in question is:
class MySerializer(serializers.Serializer):
...
And you have another serializer like this:
class OtherSerializer(serializers.Serializer):
my_field = MySerializer()
In this case, when instantiating an instance of OtherSerializer, its context is not passed automatically to MySerializer, so there would not be a request in the context of MySerializer. You can either add the context to nested serializer manually, or in the get_fields method, check that request exists in self.context and proceed accordingly.
Also, I am not sure what you are trying to accomplish, but if you provide a field with
write_only=True
in serializer class definition, the field would not be present when reading the serializer, i.e for get requests in general, which seems like what you are trying to do here. So adding the products field as write_only would have the same effect, you do not need to override get_fields method

How to serialize Inherited models in Django REST Framework

I'm working on a Django Rest Framework project, in which I have created the following models as:
from django.db import models
# Base Models...
choices = (
('Single', 'Single'),
('Multiple', 'Multiple'),
)
class UserAccountModel(models.Model):
deployment_name = models.CharField(max_length=150, blank=True)
credentials = models.FileField(upload_to='media/credentials/', name='credentials'),
project_name = models.CharField(max_length=150, blank=True)
project_id = models.CharField(max_length=100, blank=False, name='project_id')
cluster_name = models.CharField(max_length=150, blank=False)
zone_region = models.CharField(max_length=150, blank=False)
services = models.CharField(max_length=100, choices=choices)
def __str__(self):
return self.deployment_name
class AwdModel(UserAccountModel):
source_zip = models.FileField(upload_to='media/awdSource/', name='awd_source')
routing = models.TextField(name='routing', null=True)
def __str__(self):
return self.deployment_name
def save(self, **kwargs):
if not self.id and self.services == 'Multiple' and not self.routing:
raise ValidationError("You must have to provide routing for multiple services deployment.")
super().save(**kwargs)
# def clean(self):
# if self.services == 'Multiple' and self.routing is None:
# raise ValidationError('You must have to provide routing for multiple services deployment.')
class AwodModel(UserAccountModel):
source_zip = models.FileField(upload_to='media/awodSource/', name='awod_source')
routing = models.TextField({'type': 'textarea'}, name='routing')
def save(self, **kwargs):
if not self.id and self.services == 'Multiple' and not self.routing:
raise ValidationError("You must have to provide routing for multiple services deployment.")
super().save(**kwargs)
I need to serialize these models, Here's how I have implemented serializers for these models:
from rest_framework import serializers
from .models import UserAccountModel, AwdModel, AwodModel
class UserAccountSerializer(serializers.ModelSerializer):
class Meta:
model = UserAccountModel
fields = ('deployment_name', 'credentials', 'project_name',
'project_id', 'cluster_name', 'zone_region', 'services')
class AWDSerializer(serializers.ModelSerializer):
class Meta(UserAccountSerializer.Meta):
model = AwdModel
fields = UserAccountSerializer.Meta.fields + ('awd_source', 'routing',)
class AWODSerializer(serializers.ModelSerializer):
class Meta:
model = AwodModel
fields = '__all__'
But, when I try to access, AWDSerialzer it return an error as:
AttributeError at /api/v1/deployments/
Got AttributeError when attempting to get a value for field project_id on serializer AWDSerializer.
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 'project_id'.
Update: Here's my APIView code:
class DeploymentsList(APIView):
def get(self, request):
MAX_OBJECTS = int(20)
deployments = AwdModel.objects.all()[:MAX_OBJECTS]
data = AWDSerializer(deployments).data
return Response(data)
class DeploymentDetail(APIView):
def get(self, request, *args, **kwargs):
deployment = get_object_or_404(AwdModel, pk=kwargs['pk'])
data = AWDSerializer(deployment).data
return Response(data)
Help me, please!
Thanks in advance!
AttributeError at /api/v1/deployments/ Got AttributeError when
attempting to get a value for field project_id on serializer
AWDSerializer. 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 'project_id'.
This is an attribute error, when attempting to get the value from field project_id .
Get rid of the name attribute in the project_id field.
Edit The APIView code
To serialize a queryset or list of objects instead of a single object
instance, you should pass the many=True flag when instantiating the
serializer. You can then pass a queryset or list of objects to be
serialized. [Serializing multiple objects]
class DeploymentsList(APIView):
def get(self, request):
MAX_OBJECTS = int(20)
deployments = AwdModel.objects.all()[:MAX_OBJECTS]
data = AWDSerializer(deployments, many=True).data
return Response(data)
I hope this will help.
The code that you posted appears to be valid and correct. The issue however is unrelated. The exception text 'QuerySet' object has no attribute 'project_id' Refers to an issue that likely originates from your restframework app's views.py file. The exception states that you are attempting to access the attribute 'project_id' from a QuerySet.
A QuerySet is a (lazy loaded) set of models and not a single model. Even if the query set had only one element you'd still be required to access that element before accessing it's attributes.
Because you haven't shared your views.py file I can't say for sure where the issue is however here is an incorrect use case example: MyModel.objects.all().project_id. Here we can see that I am attempting to access the attribute project_id from a query set. A correct use case would be MyModel.objects.all()[0].project_id. However this assumes that the query set is not empty.
Practically, most DjangoRestFramework views inherit from rest_framework.views.APIView which subclasses django's View Class. I would suggest checking the query_set within that class is being used correctly.
Feel free to share your implementation here for further comment.
[EDIT] - After views.py coded was added.
You are attempting to serializer an entire query set with the instantiation of a serializer data = AWDSerializer(deployments).data this is causing the attribute error.
I would recommend the generics.ListAPIView class and the use of the class attributes query_set and serializer_class. These are simple to implement. You can then invoke the APIViews default get method. Here is an example for your DeploymentsList view
from rest_framework import generics
class DeploymentsList(generics.ListAPIView):
serializer_class = AWDSerializer
queryset = AwdModel.objects.all()
def get(self, request, *args, **kwargs):
MAX_OBJECTS = int(20)
self.queryset = self.queryset[:MAX_OBJECTS]
return super(DeploymentsList, self).get(request, *args, **kwargs)
[EDIT] - FileField Serialization
In order to serialize the UserAccount.credentials file field so that we serializer the path, we can use the serializers.SerializerMethodField. I.e Your UserAccountSerializer becomes:
class UserAccountSerializer(serializers.ModelSerializer):
credentials = serializers.SerializerMethodField()
def get_credentials(self, user_account):
return user_account.credentials.path
class Meta:
model = UserAccountModel
fields = ('deployment_name', 'credentials', 'project_name',
'project_id', 'cluster_name', 'zone_region', 'services')
When you inherit from a model class which is not defined as abstract in it’s own meta class, then Django creates a one-to-one relation between the subclass and its parent. Which actually creates two tables in the database; one for the base class and one for the subclass.
I haven’t tried your code, nor used Django 2, but would check using a relational field between the two serializer.

DRF #property field serializer Got AttributeError when attempting to get a value for field X on serializer Y

I'm using django rest framework for serialize and update a #property field, but i'm getting the error:
AttributeError: Got AttributeError when attempting to get a value for field `template` on serializer `PublicationSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Publication` instance.
Original exception text was: 'NoneType' object has no attribute 'template'.
i have the following models:
class Publication(models.Model):
#property
def template(self):
return self.apps.first().template
class App(models.Model):
publication = models.ForeignKey(Publication, related_name='apps')
template = models.ForeignKey(Template, blank=True, null=True)
class Template(models.Model):
name = models.CharField(_('Public name'), max_length=255, db_column='nome')
and the following serializer:
class PublicationSerializer(serializers.ModelSerializer):
template = TemplateSerializer(read_only=False)
class Meta:
model = models.Publication
fields = ('template',)
def update(self, instance, validated_data):
template_data = validated_data.pop('template', None)
instance = super().update(instance, validated_data)
if template_data:
instance.apps.all().update(template__id=template_data['id'])
return instance
This error happens when i use GET method to view and my Publication.apps is empty, and when i try to use POST method, i receive an empty OrderedDict() object.
This looks like when my field is null the DRF can't discover field type, and when my i try to POST the serializer isn't working as well...
Looks like publication you are trying to use don't have related apps. That's why self.apps.first() return None and self.apps.first().template raise exception. Try to change property to this:
#property
def template(self):
return getattr(self.apps.first(), 'template', None)

DRF's CurrentUserDefault isn't returning a user

I have a ModelSerializer. I was trying to set a user foreign key when saving a model, instantiated via the create() and update() methods of a ModelViewSet class. Eg:
ModelViewSet:
def create(self, request):
serializer = self.get_serializer(data=request.data, many=isinstance(request.data, list))
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
Serializer
def process_foreign_keys(self, validated_data):
""" Simplified for SO example """
profile = get_object_or_404(Profile, fk_user=CurrentUserDefault())
validated_data['profile'] = profile
return validated_data
def create(self, validated_data):
""" Create a Product instance. Routed from POST via DRF's serializer """
validated_data = self.process_foreign_keys(validated_data)
return Product.objects.create(**validated_data)
That code doesn't work - it throws an exception on the get_object_or_404 line:
int() argument must be a string, a bytes-like object or a number, not 'CurrentUserDefault'
If I put a few debugging statements in the ModelSerializer.create() method, I get weird stuff:
currentuser = CurrentUserDefault()
# Expect <class django.contrib.auth.models.User>, get <class 'rest_framework.fields.CurrentUserDefault'>
print("currentuser type " + str(type(currentuser)))
# Causes AttributeError: 'CurrentUserDefault' object has no attribute 'user'
print("currentuser is " + str(currentuser.__call__()))
# Causes AttributeError: 'ProductSerializer' object has no attribute 'request'
print("currentuser is " + str(self.request.user))
All this was done while a user was logged in, so it's not an AnonymousUser problem.
What am I screwing up? How do I get the current user in a serializer instantiated within the create/update methods of a ModelViewSet via self.get_serializer()?
Edit: Attempting with a HiddenField doesn't seem to work either. From the docs :
"HiddenField: This field will be present in validated_data but will not be used in the serializer output representation."
So I set as a ModelSerializer class field:
currentuser = serializers.HiddenField(default=serializers.CurrentUserDefault())
... and then attempt validated_data.get('currentuser') in the update method, and that returns None.
CurrentUserDefault is not a magic method that gets the user out of the void. It has to be within the context of a field a shown in the documentation
As #pramod pointed out, you need to either:
get_object_or_404(Profile, fk_user=self.request.user)
or set a CurrentUserDefault as a default value for a field.
def create(self, request):
serializer = self.get_serializer(data=request.data, many=isinstance(request.data, list), context={"request": request})
can you change you serializer instantiation to above code and use it as below:
profile = get_object_or_404(Profile, fk_user=self.request.user)

Categories

Resources