I got AttributeError when attempting to get a value for field client on serializer ClientSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the QuerySet instance.
models.py
class Box(models.Model):
box = models.IntegerField()
controller = models.ForeignKey(Controller, related_name='boxes', on_delete=models.CASCADE)
def __str__(self):
return str(self.box)
class Client(models.Model):
client = models.CharField(max_length=30)
cpf = models.IntegerField()
box = models.OneToOneField(
Box,
on_delete=models.CASCADE,
primary_key=True
)
def __str__(self):
return self.client
serializers.py
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = [
"id",
"client",
"cpf",
"box",
]
class BoxSerializer(serializers.ModelSerializer):
class Meta:
model = Box
fields = [
"id",
"box",
"controller"
]
views.py
class ClientViewSet(viewsets.ModelViewSet):
serializer_class = ClientSerializer
queryset = Client.objects.all()
def list(self, request, store_pk=None, locker_pk=None, controller_pk=None, box_pk=None):
queryset = Client.objects.filter(box=box_pk, box__controller=controller_pk, box__controller__locker=locker_pk, box__controller__locker__store=store_pk)
serializer = ClientSerializer(queryset, context={'request': request})
return Response(serializer.data)
def retrieve(self, request, store_pk=None, locker_pk=None, controller_pk=None, box_pk=None):
queryset = Client.objects.filter(box=box_pk, box__controller=controller_pk, box__controller__locker=locker_pk, box__controller__locker__store=store_pk)
client = get_object_or_404(queryset)
serializer = ClientSerializer(client, context={'request': request})
return Response(serializer.data)
I'm trying to get the object client on lockers/1/controllers/1/boxes/1/client/
which is OneToOneField relations with boxes and It's in a nested router
I already tried use decorator #action but yet didn't work.
Anyone know why it's not finding the correct object attribute ?
For a list method you should use many=True parameter when you're creating a new serializer instance:
serializer = ClientSerializer(queryset, context={'request': request}, many=True)
In case of retrieve only one object should be received. Instead of
client = get_object_or_404(queryset)
you should call first(), last() (or most basically and clearly - .get(pk=pk)) on queryset to retrieve only one item from QuerySet. Then you should just execute:
# client is one of elements of your queryset
serializer = ClientSerializer(client, context={'request': request})
Related
I have basically three models.
class Users(models.Model):
name = models.CharField(max_length=100)
...
class Portfolio(models.Model):
name = models.CharField(max_length=50)
user = models.ForeignKey(Users, on_delete=models.CASCADE, related_name='portfolio')
class BuySell(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name='buy_sell'
...
Any user can have multiple portfolios and portfolios can have many buy-sell. From my viewset how can I access the portfolio instance selected by the user to add buy-sell data?
In my Viewset:
class BuySellViewSet(
viewsets.GenericViewset,
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin
):
serializer_class = BuySellSerializer
queryset = BuySell.objects.all()
def get_queryset(self):
return self.request.user.portfolio
But when I add multiple portfolios for a single user I get the following error message:
TypeError at /api/v1/share/buy-sell/
Field 'id' expected a number but got <django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager object at 0x7f857c7c6940>.
How can I select the correct portfolio instance to add buy-sell data inside that instance? Also, How'd I be adding new buy-sell records in an instance of a portfolio from my viewset?
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
share = serializer.validated_data["share"]
self.perform_create(serializer)
return Response({"data": serializer.data}, status=status.HTTP_201_CREATED)
You can filter with:
class BuySellViewSet(
viewsets.GenericViewset,
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin
):
serializer_class = BuySellSerializer
def get_queryset(self):
return BuySell.objects.filter(portfolio__user=self.request.user)
I need to bulk update ("is_read" = True) Message instanses by given list of ids in one request with this code:
{"ids": [11, 4, 7]}
Model:
class Message(models.Model):
text = models.TextField(max_length=500, verbose_name=_("Text"))
sender = models.ForeignKey(
to=User,
on_delete=models.CASCADE,
related_name="sender_message",
verbose_name=_("User"),
)
thread = models.ForeignKey(
to="Thread",
on_delete=models.CASCADE,
related_name="thread_message",
verbose_name=_("Thread"),
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated"))
is_read = models.BooleanField(default=False, verbose_name=_("Is read"))
I have this serializer:
class MessageIsReadSerializer(serializers.ModelSerializer):
class Meta:
model = Message
fields = ("id", "text", "sender", "is_read")
And method in views.py:
class MessageIsRead(APIView):
permission_classes = (AllowAny,)
queryset = Message.objects.all()
def put(self, request, *args, **kwargs):
id_list = request.data['ids']
instances = []
for item in id_list:
obj = self.queryset.filter(id=item)
obj.is_read = True
instances.append(obj)
serializer = MessageIsReadSerializer(instances, many=True)
return Response(serializer.data)
urls.py
urlpatterns = [
path("messages-read/", MessageIsRead.as_view()),
]
But as a result of running this query I get an error:
AttributeError at /messages-read/
Got AttributeError when attempting to get a value for field `text` on serializer
`MessageIsReadSerializer`.
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 'text'.
What is wrong?
With help of Bartosz Stasiak I've fixed my verion of put method.
def put(self, request, *args, **kwargs):
id_list = request.data['ids']
instances = []
for item in id_list:
obj = self.queryset.get(id=item)
obj.is_read = True
obj.save()
instances.append(obj)
serializer = MessageIsReadSerializer(instances, many=True)
return Response(serializer.data)
First: here you are getting a queryset, not an instance, so later in your code you are appending querysets to the instances list. If you want to access single instance you should use get instead of filter
single_instance = self.queryset.get(id=item)
If you want to update multiple items you can use:
def put(self, request, *args, **kwargs):
id_list = request.data['ids']
instances = self.queryset.filter(id__in=id_list)
instances.update(is_read=True)
serializer = MessageIsReadSerializer(instances, many=True)
return Response(serializer.data)
I want to store an array in my database. For that I use ArrayType of this type in my model: [[[0.29, 10.27]]]. I would like to use a serializer to create my GET, POST and PUT requests.
Here is my model:
models.py
class Area(models.Model):
osm = ArrayField(
ArrayField(
ArrayField(
models.FloatField(),
size=2,
),
),
)
Here is my serializer :
serializer.py
class AreaSerializer(serializers.ModelSerializer):
class Meta:
model = Area
fields = ['osm', ]
And here is my view :
views.py
class ShopAreaList(ShopCustomListView):
"""Get or create areas for a shop"""
queryset = Shop.objects.all()
lookup_field = 'path'
def get(self, request, path):
"""Depends on mustBeLogged to get areas of a shop"""
shop = self.get_object()
areas = Area.objects.filter(shop=shop)
serializer = AreaSerializer(areas, many=True)
return Response(serializer.data)
def post(self, request, path):
"""For admin or shop owner to create areas"""
shop = self.get_object()
serializer = AreaSerializer(data=request.data)
if serializer.is_valid():
serializer.save(shop=shop)
return Response(serializer.data)
return Response(serializer.errors)
Here is the data I get:
{
"osm": [[[0.29, 10.27]]]
}
When I try to run my POST method I get this error:
Error binding parameter 0 - probably unsupported type.
I don't know what can I do to fix this error.
Thank you in advance for your help
Context
For a specific use case I need to be able to update a single field of my Visitor model using a GET request instead of a PATCH request.
My relevant Visitor model looks like this:
# models.py
class Visitor(models.Model):
visitor_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
customers = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='customer_visitors')
audiences = models.ManyToManyField(Audience, related_name='audience_visitors')
cid = models.CharField(max_length=255, unique=True)
uid = models.CharField(max_length=255)
cup = JSONField(null=True)
def __str__(self):
return self.cid
class Meta:
db_table = 'visitor'
I am using a straightforward serializer like this:
# serializers.py
class VisitorSerializer(serializers.ModelSerializer):
class Meta:
model = Visitor
fields = ('customers', 'cid', 'uid', 'cup')
I am able to update just the cup field for a specific Visitor which is looked up using the unique cid field with a PATCH like this:
# views.py
class VisitorViewSet(viewsets.ModelViewSet):
serializer_class = VisitorSerializer
queryset = Visitor.objects.all()
lookup_field = 'cid'
def list(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.serializer_class(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
Problem
The problem is that I am unable to update the cup field of a Visitor based on a given unique cid field using a GET request.
What I tried
As this answer by Uri Shalit suggested, I tried to override get_serializer() inside my VisitorViewSet and tried to use it in list() like this:
# views.py
class VisitorViewSet(viewsets.ModelViewSet):
serializer_class = VisitorSerializer
queryset = Visitor.objects.all()
lookup_field = 'cid'
def get_serializer(self, *args, **kwargs):
kwargs['partial'] = True
return super(VisitorViewSet, self).get_serializer(*args, **kwargs)
def list(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
However, updating just the cup field of a specific Visitor based on the cid field works with a PATCH request but does not update said field with a GET request. There is no error either.
Expected behaviour
Making a GET request which contains cid to identify a Visitor and cup with data that needs to be updated for the given Visitor. I know it breaks REST principles but for this use case I need to do this partial update using a GET request instead of a PATCH request.
Any help or pointers in the right direction would be much appreciated!
Add a classmethod in your model.
class Visitor(models.Model):
visitor_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
customers = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='customer_visitors')
audiences = models.ManyToManyField(Audience, related_name='audience_visitors')
cid = models.CharField(max_length=255, unique=True)
uid = models.CharField(max_length=255)
cup = JSONField(null=True)
def __str__(self):
return self.cid
class Meta:
db_table = 'visitor'
#classmethod
def update_cup(cls, cid, cup_new):
instance = cls.objects.get(cid=cid)
instance.cup = new_cup
instance.save()
In ModelViewSet override the get_queryset method, see below:
IDK how u calc new_cup I guess u get it as a queryparam
def get_queryset(self):
queryset = Visitor.objects.all()
cup_new = self.request.query_params.get('cup_new', None)
cid = self.request.query_params.get('cid', None)
[obj.update_cup(obj.cid, cup_new) for obj in queryset if obj.cid == cid]
return queryset
I recommend using an api_view to accomplish what you want. api_view is an annotation provided by the rest framework so it should be available already in your case.
#api_view(["GET"])
def update_function(request):
query_params = request.GET # Getting the parameters from request
cid = query_params["cid"]
cup = query_params["cup"]
visitor = Visitor.objects.get(cid = cid)
visitor["cup"] = cup
serializer = VisitorSerializer(data = visitor, partial=True)
if serializer.is_valid():
serializer.save()
else:
print(serializer.errors)
However I am not sure about the syntax but the approch is sufficient for your problem.
Make sure to add the function to urls.py and have a look to the documentation to get better information than mine Api Views. But dont expect it to have information about you specific problem. In your case you have to understand the api_view concept and adapt it for your needs.
I think I don't understand the problem fully. Why can't you simply override the method get_object() in your view and do custom logic in it to update the object?
def get_object(self):
obj = super().get_object()
serializer = self.get_serializer(obj, data=self.request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return obj
Trying to get a manytomany relationship in django but I'm getting the following error -
Got AttributeError when attempting to get a value for field name on serializer GenreSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Movie instance.
Original exception text was: 'Movie' object has no attribute 'name'.
There is a similar answer here recommending setting many=True, but it doesn't work -
Attribute error when attempting to get a value for field
models.py
class Genre(models.Model):
name = models.CharField(max_length=255,unique=True)
def __unicode__(self):
return self.name
class Movie(models.Model):
mname = models.CharField(max_length=255)
genres = models.ManyToManyField(Genre,related_name='movies')
def __unicode__(self):
return self.mname
serializers.py
class GenreSerializer(serializers.ModelSerializer):
class Meta:
model = Genre
fields = ('name','id')
class MovieSerializer(serializers.ModelSerializer):
genres = GenreSerializer(many=True, read_only=True)
class Meta:
model = Movie
fields = ('id','genres','mname')
urls.py
urlpatterns = [
url(r'^genres/$', views.GenreList.as_view()),
url(r'^genres/(?P<pk>[0-9]+)$', views.GenreDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
views.py
class GenreList(generics.ListCreateAPIView):
queryset = Genre.objects.all()
serializer_class = GenreSerializer
class GenreDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = GenreSerializer
def get_queryset(self):
genres = Genre.objects.get(pk=self.kwargs.get('pk', None))
movies = Movie.objects.filter(genres=genres)
return movies
If yout are using a function based serializer, and wanted to fetch all the information of the ModelSerializer. Then use many=True
#api_view()
def movie_list(request):
movies = Movie.objects.all()
serializer = MovieSerializer(movies)
print(serializer)
return Response(serializer.data)
For the above code, the response was
Got AttributeError when attempting to get a value for field name on
serializer MovieSerializer. 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 'name'.
If we just add many=True in serializer = MovieSerializer(movies, many=True)`:
#api_view()
def movie_list(request):
movies = Movie.objects.all()
serializer = MovieSerializer(movies, many=True)
print(serializer)
return Response(serializer.data)
Then the output we get is
Similar to your other question, you are using the incorrect serializer on your GenreDetail view. You need to add the serializer for the type of models you are returning from get_queryset:
class GenreDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = MovieSerializer
def get_queryset(self):
genres = Genre.objects.get(pk=self.kwargs.get('pk', None))
movies = Movie.objects.filter(genres=genres)
return movies
Let just take a simple example model in which you have three fields
from django.db import models
class Movie(models):
name = models.CharField()
description = models.TextField()
created = models.DateTimeField(auto_now_add=True)
If you're using this in serializer field then you have to pass argument "many=true" along with model name for example
movie = Movie.objects.all()
serializer = MovieSerializer(movie, many=True)
this is caused because when you have more than one objects(fields) in your model and you want to loop through every field for that we use "many=True"