It seems very simple to do nested serializer in Django REST.
But I can not find anything wrong in my code.carts does not shown up
I had followed http://www.django-rest-framework.org/api-guide/relations/#nested-relationships
Only one thing different is I use JSONField.
It should not be a problem.
class MailAPIOrderItemSerializer(serializers.ModelSerializer):
product = serializers.SerializerMethodField()
category = serializers.SerializerMethodField()
variant = serializers.SerializerMethodField()
class Meta:
model = OrderItem
fields = ('category', 'product', 'variant')
def get_variant(self, obj: OrderItem):
return obj.product.get('level_two')
def get_category(self, obj: OrderItem):
return obj.product.get('service_name')
def get_product(self, obj: OrderItem):
return obj.product.get('product_name')
class MailAPIOrderSerializer(serializers.ModelSerializer):
...
carts = MailAPIOrderItemSerializer(many=True, read_only=True)
class Meta:
model = Order
fields = (
...
'carts'
)
def get_staff_name(self, order: Order):
return order.staff.full_name
class OrderItem(AbstractSoftModelController):
order = models.ForeignKey(Order, related_name='order_items', verbose_name=_('Order'))
product = JSONField(verbose_name=_('Product'))
My Temporal Solution:
Right now. I am working around by replacing
carts = serializers.SerializerMethodField()
And in the method I use
def get_carts(self, order: Order):
qs = order.order_items.all()
serializer = MailAPIOrderItemSerializer(qs, many=True)
return serializer.data
In model Order you have backref field order_items not carts. So try change field name in MailAPIOrderSerializer:
order_items = MailAPIOrderItemSerializer(many=True, read_only=True)
class Meta:
model = Order
fields = (
...
'order_items'
)
Related
I am new to Django and I am trying to exclude a model field in nested serializer.
modals.py
class Blog(models.Model):
title = models.CharField(max_length=30)
description = models.CharField(max_length=30)
class Comment(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name="comment")
comment_bdy = models.CharField(max_length=30)
completed = models.BooleanField(default=False)
serializers.py
class BlogCommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ("id", "comment_body")
class BlogSerializer(serializers.ModelSerializer):
comment = BlogCommentSerializer(many=True)
class Meta:
model = ("id", "title", "description", "comment",)
I am trying to exclude comment which have completed=True .
I have tried many times like :-
class BlogCommentSerializer(serializers.ModelSerializer):
def to_representation(self, data):
data = data.filter(completed=False)
return super(BlogCommentSerializer, self).to_representation(data)
But It showing:
AttributeError: 'CommentReply' object has no attribute 'filter'
Then I tried using:
class BlogSerializer(serializers.ModelSerializer):
def get_comment(self, instance):
comment_instance = instance.comment_set.exclude(completed=True)
return BlogSerializer(comment_instance , many=True).data
It also didn't work.
What I am trying to do
I am trying to exclude comments which are completed=True.
You can try like this using SerializerMethodField:
class BlogSerializer(serializers.ModelSerializer):
comment = serializers.SerializerMethodField()
def get_comment(self, instance):
comment_instances = instance.comment.exclude(completed=True)
return BlogCommentSerializer(comment_instances , many=True).data
Try it:
class BlogSerializer(serializers.ModelSerializer):
comment = serializers.SerializerMethodField()
def get_comment(self, obj):
queryset = Comment.objects.
filter(blog=obj).exclude(blog__completed=True)
return [BlogCommentSerializer(q).data for q in queryset]
I want to prefetch 3 tables plus the initial table. Here are model examples of the project i am working on
class ExampleOne(models.Model):
name = models.Charfield()
option = models.BooleanField()
money = MoneyField()
that_date = models.DateTimeField()
class ExampleTwo(models.Model):
word = models.Charfield()
example_one = models.ManyToManyField(ExampleOne)
number = models.IntegerField()
class ExampleThree(models.Model):
number = models.IntegerField()
example_two = models.ForeignKey(ExampleTwo)
this_date = models.DateTimeField()
#property
def get_calculation_one(self):
if self.example_two.example_one.exists():
for example1 in self.example_two.example_one.all():
if example1.option is not None:
if self.this_date >= example1.that_date):
return self.number * example1.money.amount
else:
if self.this_date >= datetime.today()):
return self.number * example1.money.amount
else:
return 0
class ExampleFour(models.Model):
date = models.DateField()
example_three = models.ManyToManyField(ExampleThree)
#property
def get_calculation_two(self):
cost = 0
if self.example_three.exists():
for example3 in self.example_three.all():
cost += example3.get_calculation_one
return cost
return cost
Now I want to know if it is possible to retrieve data from all these models with as little hits to the database as possible because when I try to retrieve data it takes over one minute to retrieve the data and send it to the frontend
The calculation might be making many database hits in order to get the data and calculate and I think that is why it is taking more than a minute to retrieve the data
my view looks like this
qs = ExampleFour.objects.prefetch_related(
Prefetch('example_three__example_two__example_one')
).filter(...)
Is there a way to use prefetch to make the retrieval faster or is there another suggestion on I can rewrite my code in order to avoid this long retrieval time?
Note I am still new to Django
I am also using DRF so here are how the serializers look
class ExampleOneSerializer(serializers.ModelSerializer):
class Meta:
model = ExampleOne
fields = (
"name",
"option",
"money",
"that_date",
)
class ExampleTwoSerializer(serializers.ModelSerializer):
example_1_value = serializers.SerializerMethodField()
class Meta:
model = ExampleOne
fields = (
"word",
"example_one",
"number",
)
read_only_fields =(
"example_1_value",
)
def get_example_1_value(self, obj):
return ExampleOneSerializer(obj.example_one.latest('that_date')
).data
class ExampleThreeSerializer(serializers.ModelSerializer):
example_2_data = serializers.SerializerMethodField()
get_calculation_one = serializers.ReadOnlyField()
class Meta:
model = ExampleOne
fields = (
"number",
"example_two",
"this_date",
"example_2_data",
"get_calculation_one",
)
read_only_fields =(
"example_2_data",
"get_calculation_one",
)
def get_example_2_data(self, obj):
return ExampleTwoSerializer(obj.example_two).data
class ExampleFourSerializer(serializers.ModelSerializer):
example_3_data = serializers.SerializerMethodField()
get_calculation_two = serializers.ReadOnlyField()
class Meta:
model = ExampleOne
fields = (
"date",
"example_three",
"example_3_data",
"get_calculation_two",
)
read_only_fields =(
"example_3_data",
"get_calculation_two",
)
def get_example_3_data(self, obj):
return ExampleThreeSerializer(obj.example_three.all(),
many=True).data
I wonder if would prefetch work on the serializer side or would it be redundant
id field and name field not showing in result.
in models.py:
class Group(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)
admin = models.ForeignKey(User, on_delete=models.CASCADE)
member = models.ManyToManyField(User, related_name='groups_user')
def __str__(self):
return self.name
in serializers.py:
class SimpleUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id','first_name', 'last_name')
class GroupSerializer(serializers.Serializer):
admin = SimpleUserSerializer()
class Meta:
model = Group
fields = ('id','name','admin')
views.py:
#api_view(['GET'])
#permission_classes((IsAuthenticated,))
def getSomeGroup(request):
allGroup = Group.objects.all().count()
randomGroupId = random.sample(range(allGroup), 3)
randomGroup = Group.objects.filter(id__in=randomGroupId)
serializer = GroupSerializer(randomGroup, many=True)
#print(serializer)
return Response(serializer.data)
the result comes like this:
[{"admin":{"id":1,"first_name":"asif","last_name":""}},{"admin":{"id":3,"first_name":"Test2","last_name":"lastname"}},{"admin":{"id":3,"first_name":"Test2","last_name":"lastname"}}]
why id and name field not showing?
class SimpleUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
First try to access all admin
#api_view(['GET'])
#permission_classes(IsAuthenticated)
def getSomeGroup(request):
randomGroup = Group.objects.all()
serializer = GroupSerializer(randomGroup, many=True)
return Response(serializer.data)
If that works there may be issue in your these two line
The Issue may be in these two lines
allGroup = Group.objects.all().count()
randomGroupId = random.sample(range(allGroup), 3)
Modify serializers.py:
class GroupSerializer(serializers.ModelSerializer):
Here I have a model which has so many fields. So I want to use __all__ to return all the fields. But now I needed to add new field image_url so I customize a serializer like this but now with this I need to put all the model fields in the Meta class like this fields=['name','..', 'image_url'] in order to return the image_url.
Is there any way to return image_url without specifying it in the Meta.fields ?
I mean I don't want to write all the model fields in the Meta.fields (since the fields are too many) and want to return the image_url also.
serializers.py
class MySerializer(ModelSerializer):
image_url = serializers.SerializerMethodField('get_image_url')
class Meta:
model = MyModel
fields = '__all__'
def get_image_url(self, obj):
return obj.image.url
You can try to subclass te serializer:
class MySerializer(ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
class MyChildSerializer(MySerializer):
image_url = serializers.SerializerMethodField()
class Meta:
fields = MySerializer.Meta.fields + ['image_url']
def get_image_url(self, obj):
return obj.image.url
Never tried something like this, but since Meta.fields is a list you can perform basic python operations on it.
ps. If you're using pattern get_<field_name> for getter, you do not need to specify it in SerializerMethodField arguments.
Try this:
class MySerializer(ModelSerializer):
image_url = serializers.SerializerMethodField()
class Meta:
model = MyModel
fields = [f.name for f in MyModel._meta.fields] + ['image_url']
def get_image_url(self, obj):
return obj.image.url
How can I returns serval list in Rest Framework?
I have serializers.py
class HostTypeSerializer(ModelSerializer):
class Meta:
model = HostType
fields = "__all__"
class DiskOSTypeSerializer(ModelSerializer):
class Meta:
model = DiskOSType
fields = "__all__"
class DiskEssenceTypeSerializer(ModelSerializer):
class Meta:
model = DiskEssenceType
fields = "__all__"
I have the three Serializers, and I want to return data like bellow:
{
hosttypes:[the HostTypeSerializer's list data ],
diskostype:[the DiskOSTypeSerializer's list data],
diskessencetype:[the DiskEssenceTypeSerializer's list data],
}
I tried but failed, but I don't know how to do with that:
class DiskPreCreateSerialzer(ModelSerializer):
hosttypes = HostTypeSerializer(many=True, read_only=True)
diskostypes = DiskOSTypeSerializer(many=True, read_only=True)
diskessencetypes = DiskEssenceTypeSerializer(many=True, read_only=True)
class Meta:
fields = (
"hosttypes",
"diskostypes",
"diskessencetypes",
)
In views.py:
class DiskPreCreateAPIView(APIView):
serializer_class = DiskPreCreateSerialzer
permission_classes = []
...
I want to use this Serializer to returns my requirement, but failed, how can I get that?
EDIT
I don't know how to write my DiskPreCreateAPIView now, because I don't know how to get the data to return.
class DiskPreCreateAPIView(APIView):
serializer_class = DiskPreCreateSerialzer
permission_classes = []
def post(self, request):
return Response(data=xxx, status=HTTP_200_OK)
Try to use base Serializer instead of ModelSerializer:
class DiskPreCreateSerialzer(Serializer):
hosttypes = HostTypeSerializer(many=True, read_only=True)
diskostypes = DiskOSTypeSerializer(many=True, read_only=True)
diskessencetypes = DiskEssenceTypeSerializer(many=True, read_only=True)
And in your view pass dict with your lists to this serializer:
class DiskPreCreateAPIView(APIView):
serializer_class = DiskPreCreateSerialzer
permission_classes = []
def post(self, request):
...
serializer = self.serializer_class({
'hosttypes': hosttypes_qs,
'diskostype':diskostype_qs,
'diskessencetype': diskessencetype_qs,
})
return Response(data=serializer.data, status=HTTP_200_OK)