I am trying to do a fairly simple GET request that leads to a query with DRF:
def get(self, request, character):
char_entry = Dictionary.objects.filter(Q(simplified=character) | Q(traditional=character))
serializer = DictionarySerializer(char_entry)
return Response({"character": serializer.data})
My DictionarySerializer looks like this:
from rest_framework import serializers
from .models import Dictionary
class DictionarySerializer(serializers.ModelSerializer):
class Meta:
model = Dictionary
fields = ["id", "simplified", "pinyin_numbers", "pinyin_marks", "translation", "level", "traditional", ]
And my Dictionary model looks like this:
class Dictionary(models.Model):
traditional = models.CharField(max_length=50)
simplified = models.CharField(max_length=50)
pinyin_numbers = models.CharField(max_length=50)
pinyin_marks = models.CharField(max_length=50)
translation = models.TextField()
level = models.IntegerField()
class Meta:
db_table = 'dictionary'
indexes = [
models.Index(fields=['simplified', ]),
models.Index(fields=['traditional', ]),
]
As far as I can tell, this should serialize all the fields from the Dictionary table, including simplified.
Why can't Django find the attribute? What am I missing?
A QuerySet is a collection of items. It can thus contain zero, one or more items. YOu need to serialize with the many=True parameter:
def get(self, request, character):
char_entry = Dictionary.objects.filter(
Q(simplified=character) | Q(traditional=character)
)
serializer = DictionarySerializer(char_entry, many=True)
return Response({'character': serializer.data})
or if we know there is only one item, we should retrieve that single item:
from django.shortcuts import get_object_or_404
def get(self, request, character):
char_entry = get_object_or_404(
Dictionary,
Q(simplified=character) | Q(traditional=character)
)
serializer = DictionarySerializer(char_entry, many=True)
return Response({'character': serializer.data})
Related
I am new to DRF. I want to get saved the model.
In models.py, PackageDetails and PhysicalDetail have foreignkey relationship to Member
My serializers.py is as follows:
from rest_framework import serializers
from .models import Member, PackageDetails, PhysicalDetail
class PackageDetailsSerializer(serializers.ModelSerializer):
is_expired = serializers.SerializerMethodField()
members_expiry_date = serializers.SerializerMethodField()
class Meta:
model = PackageDetails
exclude = ['id']
extra_fields = ['is_expired', 'members_expiry_date']
def get_is_expired(self, instance):
return instance.is_expired
def get_members_expiry_date(self, instance):
return instance.members_expiry_date
class PhysicalDetailSerializer(serializers.ModelSerializer):
class Meta:
model = PhysicalDetail
exclude = ['id']
class MemberSerializer(serializers.ModelSerializer):
physical_details = PhysicalDetailSerializer(many=True)
package_details = PackageDetailsSerializer(many=True)
class Meta:
model = Member
fields = '__all__'
extra_fields = ['physical_details', 'package_details']
def create(self, validated_data):
physical_detail_data = validated_data.pop("physical_details")
package_detail_data = validated_data.pop("package_details")
member = Member.objects.create(**validated_data)
PhysicalDetail.objects.create(member=member, **physical_detail_data)
PackageDetails.objects.create(member=member, **package_detail_data)
return member
views.py :
class MemberViewset(viewsets.ModelViewSet):
queryset = Member.objects.all()
serializer_class = MemberSerializer
class PackageDetailViewset(viewsets.ModelViewSet):
queryset = PackageDetails.objects.all()
serializer_class = PackageDetailsSerializer
class PhysicalDetailViewset(viewsets.ModelViewSet):
queryset = PhysicalDetail.objects.all()
serializer_class = PhysicalDetailSerializer
In GET request it worked well.. but in POST request with the same json format it responses the following:
{
"physical_details": [
"This field is required."
],
"package_details": [
"This field is required."
]
}
I've provided the fields.. so why this happening..
You removed those from dict using pop()
The pop() method removes and returns an element from a dictionary having the given key.
Try using get() instead
The get() method returns the value for the specified key if the key is in the dictionary.
Trying to filter objects in my view.py to only show items (in my case Buckets) owned by Users.
I implemented the code below in my original Model [my model.py code is at the bottom of post]
class PostObjects(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status=Bucket.owner)
But I'm not sure if that is the correct procedure to list all items?
Here is the view.py where I'm trying to filter data by User aka owner. Users should ONLY be allowed to view their own items. (I will deal with permissions later)
class BucketList(generics.ListCreateAPIView):
queryset = Bucket.objects.all() #INSERT FILTER HERE
pass
Here is the model I'm referring too.
class Bucket(models.Model):
options = (
('personal', 'Personal'),
('social', 'Social'),
)
class PostObjects(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status=Bucket.owner)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='buckets')
users = models.ManyToManyField(settings.AUTH_USER_MODEL)
category = models.CharField(max_length=30, choices=options)
name = models.CharField(max_length=35)
created = models.DateTimeField(default=timezone.now)
slug = models.SlugField(unique=True, blank=True)
stock_count = models.IntegerField(blank=True, null=True)
stock_list = ArrayField(models.CharField(max_length=6),size=10)
objects = models.Manager()
postobjects = PostObjects()
class Meta:
ordering = ('-created',)
def total_stocks_calc(self):
self.stock_count = Bucket.objects.aggregate(Sum('stock_list', distinct=True))
self.save()
def get_absolute_url(self):
return reverse("bucket:bucket-view", kwargs={"slug": self.slug})
def __str__(self):
return self.stock_list
To re-state my question, how can I filter objects owned by users in class BucketList for their private view only?
UPDATE:
from django.db.models import Q
from rest_framework import generics
from bucket.models import Bucket
from .serializers import BucketSerializer
class OwnerOrUserFilterBackend(filters.BaseFilterBackend):
queryset = Bucket.postobjects.all() # wondering if you didnt write this for brevity reasons or because its not need due to the class?
def filter_queryset(self, request, queryset, view):
return queryset.filter(
Q(owner=request.user) | #do I not need to change one of the filters too request.owner?
Q(users=request.user)
)
class BucketList(generics.ListCreateAPIView):
model = Bucket
filter_backends = [OwnerOrUserFilterBackend]
You can override the get_queryset method and filter with self.request.user:
class BucketList(generics.ListCreateAPIView):
model = Bucket
def get_queryset(self, *args, **kwargs):
return super.get_queryset(*args, **kwargs).filter(
owner=self.request.user
)
For an API view, it probably is however better to define a filter, and then use this over all the API views where you want to apply this:
class IsOwnerFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset.filter(owner=request.user)
Then you can use this in your ListCreateAPIView with:
class BucketList(generics.ListCreateAPIView):
model = Bucket
filter_backends = [IsOwnerFilterBackend]
The advantage of this is that if you have other views that require the same filtering, you only need to add the IsOwnerFilterBackend as filter_backend.
Another filter could include both the owner and the users as people who can see the BucketList:
from django.db.models import Q
class OwnerOrUserFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset.filter(
Q(owner=request.user) |
Q(users=request.user)
)
then we thus filter with this filter:
class BucketList(generics.ListCreateAPIView):
model = Bucket
filter_backends = [OwnerOrUserFilterBackend]
So I'm trying to aggregate a list of colors and return the queryset to the serializer, the serialzier, however, does not seem to accept this for some reason.
When running the the commands in shell i get:
>>> from django.contrib.postgres.aggregates import ArrayAgg
>>> from inventory.models import Product
>>> products = Product.objects.filter(category__parent__name__iexact='fliser').distinct().aggregate(colors_field=ArrayAgg('colors__name'))
>>> print(products)
{'colors_field': ['Beige', 'Grå', 'Hvit', 'Sort', 'Beige', 'Gul', 'Rød']}
Which is the expected result.
The serializer is structured like this:
class ProductFiltersByCategorySerializer(serializers.ModelSerializer):
"""
A serializer to display available filters for a product lust
"""
colors = serializers.StringRelatedField(read_only=True, many=True)
class Meta:
model = Product
fields = ['colors']
The viewset looks like this:
class ProductFiltersByCategory(generics.ListAPIView):
"""
This viewset takes the category parameter from the url and returns related product filters
"""
serializer_class = ProductFiltersByCategorySerializer
def get_queryset(self):
category = self.kwargs['category']
queryset = Product.objects.filter(category__parent__name__iexact=category).distinct().aggregate(colors_field=ArrayAgg('colors__name'))
return queryset
And the relevant part of the model looks like this:
class Product(models.Model):
...
colors = models.ManyToManyField(
ProductColor,
related_name='product_color'
)
...
The error when trying to access the endpoint is 'str' object has no attribute 'colors'.
Wished output:
[
{
"colors": [
"Red",
"Orange",
],
},
]
You don't need a ListAPIView class here, Use APIView as
from rest_framework.views import APIView
from rest_framework.response import Response
class MyAPIView(APIView):
def get(self, request, *args, **kwargs):
category = kwargs['category']
agg_result = Product.objects.filter(
category__parent__name__iexact=category
).distinct().aggregate(colors_field=ArrayAgg('colors__name'))
return Response(agg_result)
class ProductColorSerializer(serializers.ModelSerializer):
class Meta:
model = ProductColor
fields = '__all__'
class ProductFiltersByCategorySerializer(serializers.ModelSerializer):
"""
A serializer to display available filters for a product lust
"""
product_color = ProductColorSerializer(many=True)
class Meta:
model = Product
fields = ['product_color']
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})
I have serialised one of my models that has a foreign key in. I get 'Parent' object is not iterable
models.py
class Parent(models.Model):
# Parent data
class Child(models.Model):
parent = ForeignKey(Parent)
serializer.py
class ChildSerializers(serializers.ModelSerializer):
parent = serializers.RelatedField(many=True)
class Meta:
model = ReportField
fields = (
'id',
'parent'
)
api.py
class ChildList(APIView):
def get(self, request, format=None):
child = Child.objects.all()
serialized_child = ChildSerializers(child, many=True)
return Response(serialized_child.data)
Im guessing i have to pass the parent list to the child list but not sure of the best way to do it
attempt api.py
class ChildList(APIView):
def get(self, request, format=None):
child = Child.objects.all()
parent = Parent.objects.all()
serialized_child = ChildSerializers(child, many=True)
serialized_parent = ChildSerializers(parent, many=True)
return Response(serialized_child.data, serialized_parent.data)
Why using many=True. Parent is just a single field, no need to use explicit serializer field. Just get rid of these many=True
-answered by mariodev in comment.
You can do something like this using python collections as an intermediate
#serializers.py
class TimelineSerializer(serializers.Serializer):
childs= childSerializer(many=True)
parents = parentSerializer(many=True)
#apiviews.py
from collections import namedtuple
Timeline = namedtuple('Timeline', ('childs', 'parents'))
def list(self, request):
timeline = Timeline(
childs=Child.objects.all(),
parents=Parent.objects.all(),
)
serializer = TimelineSerializer(timeline)
return Response(serializer.data)
If your using a ModalSerializer then you have two foreign keys in a modal, then use "to_representation" function like this
def to_representation(self, instance):
rep = super().to_representation(instance)
rep['pcatalog_name'] = CatalogSerializer(instance.pcatalog_name).data
rep['pcategory_name'] = CategorySerializer(instance.pcategory_name).data
return rep
Replace ForeignKey by ManyToManyField to clarify the serializer field with many = True