How to reorder serializer response in django rest framework? - django-rest-framework

Currently I an getting following response, because of nested serializer. I have nested product serializer inside cart serializer. By doing like that, I get following result. But want all attributes of product in main section(outside the product, as shown below)
{
"product": {
"id": 1,
"name": "Ghost Peanut Butter Cereal Milk Whey Protein",
"product_code": "B07FLJYP5M",
"description": "Ghost products feature a 100% transparent label that fully discloses the dose of each active ingredient.",
"price": "5000.00",
"photo": "https://images-na.ssl-images-amazon.com/images/I/61WZazUpWsL._SX522_.jpg",
"link_to_amazon": "https://www.amazon.com/dp/B07FLJYP5M/?tag=1230568-20"
},
"description": null,
"default": "Yes"
}
But I want the response like below:
{
"name": "Ghost Peanut Butter Cereal Milk Whey Protein",
"product_code": "B07FLJYP5M",
"description": "Ghost products feature a 100% transparent label that fully discloses the dose of each active ingredient.",
"price": "5000.00",
"photo": "https://images-na.ssl-images-amazon.com/images/I/61WZazUpWsL._SX522_.jpg",
"link_to_amazon": "https://www.amazon.com/dp/B07FLJYP5M/?tag=1230568-20",
"description": null,
"default": "Yes"
}
models.py
class DefaultCart(models.Model):
# Default Cart in Model class
YES = 'Yes'
NO = 'No'
DEFAULT_CHOICES = (
(YES, 'Yes'),
(NO, 'No'),
)
product = models.ForeignKey(Product, related_name='product', on_delete=models.CASCADE)
description = models.TextField(blank=True, null=True)
default = models.CharField(
max_length=3,
choices=DEFAULT_CHOICES,
default=YES,
)
serializers.py
class ProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Product
fields=(
'id','name','product_code','description','price','photo','link_to_amazon'
)
class DefaultCartSerializer(serializers. HyperlinkedModelSerializer):
product = ProductSerializer(read_only=True)
class Meta:
model = DefaultCart
fields = (
'product',
'description',
'default'
)
read_only_fields = ('id',)
views.py
def index(request):
# retrive all default_carts or create new default_cart
if request.method == 'GET':
default_carts = DefaultCart.objects.all()
serializer = DefaultCartSerializer(default_carts, many=True)
return Response(serializer.data)

Here I have solved my problem by doing following in serializers.py
class DefaultCartSerializer(serializers. ModelSerializer):
product_id = serializers.IntegerField(
required=True,
validators=[UniqueValidator(queryset=DefaultCart.objects.all())]
)
name = serializers.SerializerMethodField('get_product_name')
product_code = serializers.SerializerMethodField('get_product_product_code')
price = serializers.SerializerMethodField('get_product_price')
photo = serializers.SerializerMethodField('get_product_photo')
link_to_amazon = serializers.SerializerMethodField('get_product_link_to_amazon')
class Meta:
model = DefaultCart
fields = (
'id',
'product_id',
'description',
'default',
'name',
'product_code',
'price',
'photo',
'link_to_amazon'
)
read_only_fields = ('id',)
def get_product_name(self, obj):
return obj.product.name
def get_product_product_code(self,obj):
return obj.product.product_code
def get_product_price(self, obj):
return obj.product.price
def get_product_photo(self, obj):
return obj.product.photo
def get_product_link_to_amazon(self, obj):
return obj.product.link_to_amazon

Related

Why list of related items is not showing in API?

Field list_items is not showing in the API request.
I want to create request to get TodoItem with list of ListItem objects related to TodoItem.
I tried to change types of list_items value to another, but it isn't working.
Response right now:
[
{
"id": 1,
"type": "L",
"title": "Some title",
"due_date": null,
"created_at": "2022-08-02T09:26:07.149081Z",
"text_value": "",
"url_value": ""
}
]
Response I need:
[
{
"id": 1,
"type": "L",
"title": "Some title",
"due_date": null,
"created_at": "2022-08-02T09:26:07.149081Z",
"text_value": "",
"url_value": ""
"list_items":[
Item #1,
Item #2,
]
}
]
models.py
class TodoItem(models.Model):
id = models.AutoField(primary_key=True)
type = models.CharField(max_length=1, choices=(
('T', 'String'), ('L', 'List'), ('U', 'URL')))
title = models.CharField(max_length=200, blank=False, null=False)
due_date = models.DateField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
text_value = models.TextField(blank=True, max_length=500)
url_value = models.URLField(blank=True) # TODO: add validator/test it
class ListItem(models.Model):
id = models.AutoField(primary_key=True)
value = models.CharField(max_length=100)
checked = models.BooleanField(default=False)
todo_item = models.ForeignKey(TodoItem, on_delete=models.CASCADE)
position = PositionField(default=0, unique_for_fields=('todo_item',))
serializers.py
class ListItemSerializer(serializers.ModelSerializer):
todo_item = serializers.PrimaryKeyRelatedField(
queryset=TodoItem.objects.all(), required=False, allow_null=True, default=None)
class Meta:
model = ListItem
fields = ['id', 'value', 'position', 'todo_item']
class TodoItemSerializer(serializers.ModelSerializer):
created_at = django_filters.DateFromToRangeFilter()
def get_list_items(self, obj):
return ListItemSerializer(many=True, read_only=True).data
class Meta:
model = TodoItem
fields = '__all__'
I think you need to set the related_name in the ForeignKey field.
class ListItem(models.Model):
...
todo_item = models.ForeignKey(TodoItem, on_delete=models.CASCADE, related_name = "list_items")
position = PositionField(default=0, unique_for_fields=('todo_item',))
Then in the serializer, you can set the list_items field.
class TodoItemSerializer(serializers.ModelSerializer):
created_at = django_filters.DateFromToRangeFilter()
list_items = ListItemSerializer(many= True, read_only = True)
class Meta:
model = TodoItem
fields = ('id', 'type', 'title', 'due_at', 'created_at', 'text_value', 'url_value', 'list_items', )

Filtering nested views in DRF, how to implement?

There is a product serializer:
class ProductSerializer(serializers.ModelSerializer):
category = serializers.SlugRelatedField(slug_field="name", read_only=True)
class Meta:
model = Product
fields=(
'id',
'name',
'category',
'subcategory',
'manufacturer',
'get_absolute_url',
'description',
'price',
'get_image',
'get_thumbnail'
)
As well as a category serializer that generates a nested representation of the products associated with this category:
class CategorySerializer(serializers.ModelSerializer):
products = ProductSerializer(many=True)
class Meta:
model = Category
fields = (
'id',
'name',
'get_absolute_url',
'products'
)
Sample Data:
[
{
"id": 4,
"name": "Notebook",
"get_absolute_url": "/notebooks/",
"products": [
{
"id": 3,
"name": "HP for work",
"category": "Notebook",
"subcategory": "Working",
"manufacturer": "HP",
"get_absolute_url": "/notebooks/hp_2000/",
"description": "desc",
"price": "25000.00",
"get_image": "http://127.0.0.1:8000/media/uploads/slidebar-image1.jpg",
"get_thumbnail": "http://127.0.0.1:8000/media/uploads/uploads/slidebar-image1.jpg"
},
{
"id": 1,
"name": "Acer for game",
"category": "Notebook",
"subcategory": "Gamers",
"manufacturer": "Acer",
"get_absolute_url": "/notebooks/acer_for_game/",
"description": "Desc",
"price": "50000.00",
"get_image": "http://127.0.0.1:8000/media/uploads/AcerAspire_6m9A45R.jpg",
"get_thumbnail": "http://127.0.0.1:8000/media/uploads/uploads/AcerAspire_6m9A45R.jpg"
}
]
}
]
I can also display filtered products in the URL using query parameters, for example, by subcategory:
url = http://127.0.0.1:8000/api/products/query-product/?sub=Gamers:
[
{
"id": 1,
"name": "Acer for game",
"category": "Notebook",
"subcategory": "Gamers",
"manufacturer": "Acer",
"get_absolute_url": "/notebooks/acer_for_game/",
"description": "Desc",
"price": "50000.00",
"get_image": "http://127.0.0.1:8000/media/uploads/AcerAspire_6m9A45R.jpg",
"get_thumbnail": "http://127.0.0.1:8000/media/uploads/uploads/AcerAspire_6m9A45R.jpg"
}
]
This is where the product is filtered:
class ProductQueryList(generics.ListAPIView):
serializer_class = ProductSerializer
def get_queryset(self):
queryset = Product.objects.all()
subcategory_name = self.request.query_params.get('sub')
if subcategory_name is not None:
queryset = queryset.filter(subcategory = subcategory_name)
return queryset
I'm stuck at the moment with filtering for categories. Here I either pass a queryset containing a specific category, or pass the filtered products associated with this category to the same queryset:
class CategoryDetail(generics.ListAPIView):
serializer_class = CategorySerializer
def get_queryset(self):
category = self.kwargs['category_slug']
# queryset = Category.objects.get(slug=category)
# subcategory_name = self.request.query_params.get('sub')
# # if subcategory_name is not None:
# queryset = queryset.products.filter(subcategory = subcategory_name)
return Category.objects.filter(slug=category)
Is there any way to pass something like queryset.products to the nested products list? Those somehow pass the category to the queryset, and override the products nested in it.
You can use a Prefetch object here to achieve the nested filtering, something like this:
class CategoryDetail(generics.ListAPIView):
serializer_class = CategorySerializer
def get_queryset(self):
category = self.kwargs['category_slug']
queryset = Category.objects.get(slug=category)
subcategory_name = self.request.query_params.get('sub')
if subcategory_name is not None:
prefetch_filtered_products = Prefetch(
'products',
Product.objects.filter(subcategory=subcategory_name)
)
return queryset.prefetch_related(prefetch_filtered_products)
return queryset.prefetch_related('products')
This should also save you from making extra queries to the database.

DRF nested serializer - object has no attribute

I am a newbie with django rest framework and python. and it is first time I asking a question here. thanks for helps from now..
--------------my models
class defined_views(models.Model):
name = models.CharField(max_length=150)
class defined_permissions(models.Model):
name = models.CharField(max_length=150)
class menuler(models.Model):
adi= models.CharField(max_length=150, null=True)
sira= models.PositiveIntegerField(null=True, blank=True)
parentId= models.ForeignKey('self', null=True, on_delete=models.CASCADE)
viewId= models.ForeignKey(defined_views, null=True, on_delete=models.CASCADE)
menu_type= models.CharField(max_length=150, null=True)
class user_menu_permission(models.Model):
userId = models.ForeignKey(User, null=False, on_delete=models.CASCADE)
menuID = models.ForeignKey(menuler, null=False, on_delete=models.CASCADE)
permissionID = models.ForeignKey(defined_permissions, null=True, on_delete=models.CASCADE)
-------------------serializer
class menulerSerializer(serializers.ModelSerializer):
class Meta:
model = menuler
fields = ['id', 'adi', 'sira', 'parentId', 'viewId', 'menu_type']
class user_menu_permissionSerializer(serializers.ModelSerializer):
menu = menulerSerializer()
class Meta:
model = user_menu_permission
fields = ['menuID', 'permissionID', 'menu']
class UserSerializer(serializers.ModelSerializer):
menuler = user_menu_permissionSerializer(
source='user_menu_permission_set', many=True)
class Meta:
model = User
fields = ['id', 'username', 'menuler']
------------------and view
class userView(APIView):
def get(self, request):
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
I get this errror
"Got AttributeError when attempting to get a value for field menu on serializer user_menu_permissionSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the user_menu_permission instance.
Original exception text was: 'user_menu_permission' object has no attribute 'menu'."
if I change user_menu_permissionSerializer like that
class user_menu_permissionSerializer(serializers.ModelSerializer):
class Meta:
model = user_menu_permission
fields = ['menuID', 'permissionID']
I get result like that bu I want get menu as tree also.. but unfortunetly...
[
{
"id": 1,
"username": "admin",
"menuler": []
},
{
"id": 2,
"username": "taner.kader",
"menuler": [
{
"menuID": 2,
"permissionID": 1
},
{
"menuID": 3,
"permissionID": 1
},
{
"menuID": 5,
"permissionID": 1
}
]
}
]
I have solved like below
models
class defined_views(models.Model):
name = models.CharField(max_length=150)
class defined_permissions(models.Model):
name = models.CharField(max_length=150)
class menuler(models.Model):
adi= models.CharField(max_length=150, null=True)
sira= models.PositiveIntegerField(null=True, blank=True)
parentId= models.ForeignKey('self', null=True, on_delete=models.CASCADE)
viewId= models.ForeignKey(defined_views, null=True, on_delete=models.CASCADE)
menu_type= models.CharField(max_length=150, null=True)
permissionID = models.ManyToManyField(defined_permissions, through='user_menu_permission')
userId = models.ManyToManyField(User, through='user_menu_permission')
class user_menu_permission(models.Model):
# userId = models.ForeignKey(User, related_name='kullanici_menuler', null=False, on_delete=models.CASCADE)
userId = models.ForeignKey(User, null=False, on_delete=models.CASCADE)
menuID = models.ForeignKey(menuler,null=False, on_delete=models.CASCADE)
permissionID = models.ForeignKey(defined_permissions, null=True, on_delete=models.CASCADE)
serializers
class defined_permissionsSerializer(serializers.ModelSerializer):
class Meta:
model = defined_permissions
fields = ['id', 'name']
class defined_viewsSerializer(serializers.ModelSerializer):
class Meta:
model = defined_views
fields = ['id', 'name']
class menulerSerializer(serializers.ModelSerializer):
class Meta:
model = menuler
fields = ['id', 'adi', 'sira', 'parentId', 'viewId', 'menu_type']
# fields = ['adi']
class user_menu_permissionSerializer(serializers.ModelSerializer):
# menu = menulerSerializer(many=True)
class Meta:
model = user_menu_permission
fields = ['menuID', 'permissionID']
class UserSerializer(serializers.ModelSerializer):
# menuler = user_menu_permissionSerializer(
# source='user_menu_permission_set', many=True)
# kullanici_menuler = user_menu_permissionSerializer(many=True)
kullanici_menuler= menulerSerializer(source='menuler_set', many=True)
class Meta:
model = User
fields = ['id', 'username', 'kullanici_menuler']
and result
[
{
"id": 1,
"username": "admin",
"kullanici_menuler": []
},
{
"id": 2,
"username": "tayfun.uzun",
"kullanici_menuler": [
{
"id": 2,
"adi": "Yapılacaklar Listesi",
"sira": 1,
"parentId": 1,
"viewId": 1,
"menu_type": "view"
},
{
"id": 3,
"adi": "Raporlamalar",
"sira": 2,
"parentId": 1,
"viewId": 2,
"menu_type": "view"
},
{
"id": 5,
"adi": "Süreç İşleyişleri",
"sira": 1,
"parentId": 4,
"viewId": 3,
"menu_type": "view"
}
]
}
]
Try below something like this:
serializer.py :
class user_menu_permissionSerializer(serializers.ModelSerializer):
menu = menulerSerializer(many=True)
class Meta:
model = user_menu_permission
fields = ['menuID', 'permissionID', 'menu']
views.py :
class userView(APIView):
def get(self, request):
data = User.objects.all()
serializer = UserSerializer(data, many=True)
return Response(serializer.data)

How to filter nested serializers in django rest framework

I have a viewset like this:
class PizzaViewSet(viewsets.ModelViewSet):
queryset = PizzaCategory.objects.all()
serializer_class = PizzaCategorySerializer
model = PizzaCategory
My serializers are:
class PizzaCategorySerializer(CustomModelSerializer):
pizzas = PizzaSerializer(many=True)
class Meta:
model = PizzaCategory
fields = "__all__"
class PizzaSerializer(CustomModelSerializer):
images = PizzaImageSerializer(many=True, read_only=True)
class Meta:
model = Pizza
fields = "__all__"
My endpoint shows something like this:
[
{
"id": 1,
"pizzas": [
{
"id": 1,
"images": [
{
"id": 1,
"image": "http://127.0.0.1:8000/media/images/305ced93-8f6.jpg",
"title": "pizza.jpg",
}
],
"price": "10.00",
"name": "my pizza example",
"category": 1
}
],
"name": "big cheese",
},
...
My problem: I need to filter my result by price (example, price equal to 5).
In my mind, something like this should work:
pizzas = PizzaSerializer(Pizzas.objects.filter(price=5), many=True)
But no, nothing change. Can I how to filter correctly this?
class PizzaCategorySerializer(CustomModelSerializer):
pizzas = serializers.SerializerMethodField()
class Meta:
model = PizzaCategory
fields = "__all__"
def get_pizzas(self, obj):
try:
queryset = Pizzas.objects.filter(price=5)
serializer = PizzaSerializer(queryset, many=True)
return serializer.data
except:
return None

Django Rest Framework - filtering nested serializer

One Book can have many BookContent in different languages. In my example, the Book has 2 BookContents, one in english and the other in chinese.
I am getting the Book and its content in a specific language by specifying the language queryset like this : http://localhost/api/books/?language=english. When this is call, it calls BookList get_query in views.py where I filter the content by language. However, the json results still print all the BookContent for that Book.
Below are my codes.
models.py
class Book(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=255)
class BookContent(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
book = models.ForeignKey(Book, releated_name='content' on_delete=models.CASCADE)
content = models.TextField()
language = models.TextField()
serializers.py
class BookContentSerializer(serializers.ModelSerializer):
class Meta:
model = BookContent
fields = ('id', 'content', 'language')
class BookSerializer(serializers.ModelSerializer):
content = BookContentSerializer(many=True, read_only=True)
class Meta:
model = Book
fields = ('id', 'title', 'content')
views.py
class BookList(generics.ListAPIView):
serializer_class = BookSerializer
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
if 'language' in self.request.query_params:
language = self.request.query_params['language']
return Book.objects.filter(content__language=language).distinct().order_by('title')
class BookDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = (permissions.IsAuthenticated,)
Current Json results:
{
"results": [
{
"id": "d3e5185a-1b7b-427c-bbe3-030bfa2e3bce",
"title": "My Book Title",
"book_content": [
{
"id": "0fea8027-3ecf-4571-a95f-5a09a93408ec",
"content": "hello content 1",
"language": "english"
},
{
"id": "0fea8027-3ecf-4571-a95f-5a09a93408ed",
"content": "你好",
"language": "chinese"
}
]
}
]
}
How do I get a list of Books and the BookContent language for the specific language only?
Expected Json results:
{
"results": [
{
"id": "d3e5185a-1b7b-427c-bbe3-030bfa2e3bce",
"title": "My Book Title",
"book_content":
{
"id": "0fea8027-3ecf-4571-a95f-5a09a93408ec",
"content": "hello content 1",
"language": "english"
}
}
]
}

Resources