Django Rest Framework Serializer format - django

I have two serializers: one for the Restaurant model, another for the MainMenu model:
class RestaurantSerializer(serializers.ModelSerializer):
class Meta:
model = Restaurant
class MainMenuSerializer(serializers.ModelSerializer):
restaurant = RestaurantSerializer()
main_menu_items = serializers.StringRelatedField(many=True)
class Meta:
model = MenuMain
fields = ('id', 'restaurant', 'main_menu_items')
The current output of the MainMenuSerializer is
[
{
"id": 1,
"restaurant": {
"id": 1,
"name": "Restaurant A",
"location": "Street B"
},
"main_menu_items": [
"Fried Rice"
]
},
{
"id": 2,
"restaurant": {
"id": 1,
"name": "Restaurant A",
"location": "Street B",
},
"main_menu_items": [
"Noodles"
]
}
]
But I want the RestaurantSerializer to only output once, something like this:
[
{
"restaurant": {
"id": 1,
"name": "Restaurant A",
"location": "Street B"
}
},
[
{
"id": 1,
"main_menu_items": [
"Fried Rice",
"Meat Balls"
]
},
{
"id": 2,
"main_menu_items": [
"Noodles"
]
}
]
]
EDIT:
models used
class Restaurant(models.Model):
name = models.CharField(max_length=100, default='')
location = models.CharField(max_length=100, default='')
class MenuMain(models.Model):
price = models.IntegerField()
restaurant = models.ForeignKey(Restaurant, related_name='main_menus')
class MenuMainItems(models.Model):
menu_main = models.ForeignKey(MenuMain, related_name='main_menu_items')
item = models.CharField(max_length=150, default='')

The more "Django REST" way of doing this is to set up a url node for each restaurant that returns all the menu items in the restaurant. For instance,
urls.py
url(r'restaurant-menu/(?P<pk>[0-9]+)$', MenuItemViewset.as_view())
In your viewset class
class MenuItemViewset(viewsets.ModelViewSet):
serializer_class = MainMenuSerializer
def retrieve(self, request, pk=None):
return Restaurant.objects.get(pk=pk).menumain_set.all()[0].menumainitems_set.all()
This of course assumes one menu per restaurant. If there are multiple menus and you want to get all of them, it's probably best practice to break it up into two calls: one for the restaurant's menu, and to get a particular menu's items. Otherwise too many assumptions are being made about the organization of the data, and that's a rather fragile design pattern.
Link to docs on fetching related objects
If you still are intent on getting a response like you originally asked for, you just need to reverse the serializer nesting, i.e. add a menu field to the RestaurantSerializer.

Here is the very simplest approach I came with. I know this can be improved.
Please comment if you have any query or improvement suggestion.
class RestaurantSerializer(serializers.ModelSerializer):
menu = serializers.SerializerMethodField()
def get_menu(self, obj):
dict_l = {}
res = MainMenu.objects.all() # filter as per your requirement
'''
flds = MainMenu._meta.local_fields
for ins in res:
for f in flds:
print f.name
'''
for ins in res:
dict_l['id'] = ins.id
dict_l['name'] = ins.name
dict_l['restaurant'] = ins.restaurant_id
# return {1: 1, 2: 2}
return dict_l
class Meta:
model = Restaurant
fields = ('id', 'name', 'menu',)

Related

How to show subcategories under category type

I need to get the child list under the parent list as a group.
class ServiceSerializer(serializers.ModelSerializer):
cleaning_type = serializers.CharField(source='cleaning_type.cleaning_type_name')
class Meta:
model = Service
fields = ('id', 'cleaning_type','service_name')
class ServiceTypeViewSet(ModelViewSet):
serializer_class = ServiceSerializer
http_method_names = ["get"]
queryset = Service.objects.all()
def get_queryset(self):
"""
This view should return a list of all the service types.
"""
servicename_list = Service.objects.all()
return servicename_list
It shows:
[
{
"id": 1,
"cleaning_type": "Lite service",
"service_name": "Floors",
},
{
"id": 2,
"cleaning_type": "Lite service",
"service_name": "Bathrooms",
},
{
"id": 3,
"cleaning_type": "Lite service",
"service_name": "Kitchen",
}
]
I want this to be in the following format:
[
{
id: 1,
cleaning_type: 'Lite service',
service_name: ['Floors', 'bathroom', 'kitchen'],
},
{
id: 2,
cleaning_type: 'Moving cleaning',
service_name: ['Kitchen Including All Appliances And Cabinets'],
},
]
That means all child elements will be under a separate parent list. Not separate by separate.
models.py is here:
Cleaning Type Model:
class CleaningType(models.Model):
cleaning_type_name = models.CharField(
_("Select Cleaning Type"), blank=True, null=True, max_length=255)
price = models.DecimalField(default=0,max_digits=6, decimal_places=2)
def __str__(self):
return self.cleaning_type_name
Service Model:
class Service(models.Model):
cleaning_type = models.ForeignKey(
CleaningType, on_delete=models.CASCADE)
service_name = models.CharField(
_("Service Name"), blank=True, null=True, max_length=255)
#string type added
def __str__(self):
return str(self.service_name)
I want sub categories under parent caterories. Here cleaning_type is the parent category and service is the child category of cleaning_type. i.e : cleaning_type >> service_type
I'd create a view for the parent category and then get child categories for each parent category. First, you should create a serializer for CleaningType model:
class CleaningTypeSerializer(serializers.ModelSerializer):
service_types = serializers.SerializerMethodField('get_service_types')
def get_service_types(self, cleaning_type_name):
return Service.objects.filter(cleaning_type=cleaning_type_name).values_list("service_name", flat=True)
class Meta:
model = CleaningType
fields = "__all__"
Then, create a view using the new serializer:
class CleaningTypesViewSet(ModelViewSet):
serializer_class = CleaningTypeSerializer
http_method_names = ["get"]
queryset = CleaningType.objects.all()
The response looks something like this:
[
{
"id": 1,
"service_types": [
"Moving Service 1",
"Moving Service 2",
"Moving Service 3"
],
"cleaning_type_name": "Moving Cleaning",
"price": "200.00"
},
{
"id": 2,
"service_types": [
"Another Service 1",
"Another Service 2"
],
"cleaning_type_name": "Another Cleaning",
"price": "300.00"
}
]

How to have a different models.ForeignKey related_name for each linked objects of same model?

I’m working with Django Rest to have multiple “text block” objects linked with the document object. I’ve accomplished this with a simple models.ForeignKey feature so far.
However, I’m rendering all of these text blocks in multiple columns in the front end.
The Textblock model will have a column field to determine which goes to which column. Since the order of these text blocks matter, I was afraid of having them all mixed together under single "all_columns" field
So far, I figured the easiest way is to let DRF return something like the following:
{
"name": "Comparing Two Characters",
"column_A": [
{
"title": "Text Block",
"body": "lorem ipsum blah blah"
"col": 1
}
],
"column_B": [
{
"title": "Text Block 2",
"body": "lorem ipsum blah blah"
"col": 2
},
{
"title": "Text Block 3",
"body": "lorem ipsum blah blah"
"col": 2
}
]
}
How would I be able to implement something like this? I’m not sure if using related fields is even ideal for such cases. I would appreciate any help!
Here’s my current models.py code for reference:
class Document(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
# other fields
def __str__(self):
return self.name
class TextBlock(models.Model):
id = models.AutoField(primary_key=True)
document = models.ForeignKey(Document, related_name='blocks', on_delete=models.CASCADE)
col = models.IntegerField()
title = models.CharField(max_length=100)
body = models.CharField(max_length=100)
Edit:
What I'm getting returned with the updated code by sayeed910
"name": "outlineblock-test",
"desc": "",
"blocks": [
{
"url": "http://0.0.0.0:8000/api/v1/layoutblocks/7/",
"col": 3,
"title": "col3",
"placeholder": "hehehe",
"template": "http://0.0.0.0:8000/api/v1/templates/3/"
},
{
"url": "http://0.0.0.0:8000/api/v1/layoutblocks/6/",
"col": 2,
"title": "col2",
"placeholder": "hehe",
"template": "http://0.0.0.0:8000/api/v1/templates/3/"
},
{
"url": "http://0.0.0.0:8000/api/v1/layoutblocks/5/",
"col": 1,
"title": "col1",
"placeholder": "haha",
"template": "http://0.0.0.0:8000/api/v1/templates/3/"
}
],
serializers.py
class TextBlockSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TextBlock
fields = '__all__'
class DocumentSerializer(serializers.HyperlinkedModelSerializer):
blocks = LayoutBlockSerializer(many=True, read_only=True)
class Meta:
model = Document
fields = '__all__'
Try this:
from collections import defaultdict
class Document(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
# other fields
def __str__(self):
return self.name
def blocks_by_column(self):
block_group = defaultdict(list)
for block in self.blocks.all():
block_group[block.col].append(block)
return block_group
class TextBlock(models.Model):
id = models.AutoField(primary_key=True)
document = models.ForeignKey(Document, related_name='blocks', on_delete=models.CASCADE)
col = models.IntegerField()
title = models.CharField(max_length=100)
body = models.CharField(max_length=100)
If you have a ordering mechanism i.e. an order column, you can change self.blocks.all() to self.blocks.order_by(<column>).all(). You can later change the keys of blocks_group as you see fit. 1 -> column_A.
As Per OP's Edit:
You should perform the grouping operation in the serializer instead of the model
from collections import defaultdict
class DocumentSerializer(serializers.HyperlinkedModelSerializer):
blocks = LayoutBlockSerializer(many=True, read_only=True)
def to_representation(self, instance):
ret = super().to_representation(instance)
block_group = defaultdict(list)
for block in ret["blocks"]:
block_group[block["col"]].append(block)
ret["blocks"] = dict(block_group)
return ret
class Meta:
model = Document
fields = '__all__'

Django Count with filter

iam trying to count with filter and it doesn't show any error but there is no count showing
I have tried all what I know and search every where didn't find any solution :
class UsersAnswersSerializer(serializers.ModelSerializer):
Answers = serializers.SerializerMethodField('get_answers')
def get_answers(self, obj):
queryset = AnswersModel.objects.filter(Question=obj.Answer.Question.id)\
.annotate(answersCount=Count('UsersAnswer', distinct=True))
serializer = Answersserializer(instance=queryset, many=True)
return serializer.data
class Meta:
model = UsersAnswerModel
fields = ['Answer', 'APNSDevice' ,'RegistrationID','Answers']
and this is my models :
class AnswersModel(models.Model):
Question = models.ForeignKey(QuestionsModel, related_name='QuestionAnswer', on_delete=models.CASCADE)
Answer = models.CharField(max_length=200)
class UsersAnswerModel(models.Model):
Answer = models.ForeignKey(AnswersModel, related_name='UsersAnswer', on_delete=models.CASCADE)
RegistrationID = models.CharField(max_length=200)
APNSDevice = models.CharField(max_length=200,default='no name')
class QuestionsModel(models.Model):
created = models.DateTimeField(auto_now_add=True)
Question = models.CharField(max_length=200)
what is get is
{
"Answer": 12,
"APNSDevice": "byname",
"RegistrationID": "asdasdasdasdasdasdasdasdsa",
"Answers": [
{
"id": 10,
"Question": 4,
"Answer": "Answer 1",
"UsersAnswer": [
"gfgdgdfgdf",
"c748dfd8aa7dd73a6c1ef17676aa7667161ff7e0f8e2ef21ef17e964a26150e4"
]
},
{
"id": 11,
"Question": 4,
"Answer": "Answer 2",
"UsersAnswer": [
"sfgdfgdf",
"c748dfd8aa7dd73a6c1ef17676aa7667161ff7e0f8e2ef21ef17e964a26150e4",
"c748dfd8aa7dd73a6c1ef17676aa7667161ff7e0f8e2ef21ef17e964a26150e4",
"c748dfd8aa7dd73a6c1ef17676aa7667161ff7e0f8e2ef21ef17e964a26150e4"
]
}
I need to make "UsersAnswer" show a count of its array
I think what you want to do is aggregate and count.
Replace annotate with aggregate

Django - Retrieve nested fields multiple levels deep using foreign keys

I'm struggling to write a Django GET that returns the following looking response:
{
"lists": [
{
"id": "123",
"list_order": [
{
"id": "123_1",
"order": 1,
"list_id": "123",
"item_id": 9876,
"item": {
"id": 9876,
"name": "item1",
"location": "California"
}
},
{
"id": "123_2",
"order": 2,
"list_id": "123",
"item_id": 2484,
"item": {
"id": 2484,
"name": "item2",
"location": "California"
}
}
],
"updated_date": "2018-03-15T00:00:00Z"
}
]
}
Given a list_id, the response returns the basic information on the list ("id", "updated_date"), as well as the order of items in the list. Inside each item in the list order, it also grabs the related item details (nested in "item"). I'm able to get this response without the "item" details ("id", "name", "location" fields) and with no error:
{
"lists": [
{
"id": "123",
"list_order": [
{
"id": "123_1",
"order": 1,
"list_id": "123",
"item_id": 9876
},
{
"id": "123_2",
"order": 2,
"list_id": "123",
"item_id": 2484
}
],
"updated_date": "2018-03-15T00:00:00Z"
}
]
}
Again there is no error, and I can retrieve the first nested level without any issue. The problem is getting the "item" information to show within each "list_order". Below are my models, serializers, and views.
models.py
class Lists(models.Model):
id = models.CharField(null=False, primary_key=True, max_length=900)
updated_date = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'tbl_lists'
class Items(models.Model):
id = models.BigIntegerField(primary_key=True)
name = models.TextField(blank=True, null=True)
location = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'tbl_items'
class ListOrder(models.Model):
id = models.CharField(null=False, primary_key=True, max_length=900)
list_id = models.ForeignKey(Lists, db_column='list_id', related_name='list_order')
item_id = models.ForeignKey(Items, db_column='item_id', related_name='items')
order = models.BigIntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'tbl_list_order'
serializers.py
class ItemsSerializer(serializers.ModelSerializer):
class Meta:
model = Items
fields = '__all__'
class ListOrderSerializer(serializers.ModelSerializer):
item = ItemsSerializer(many=False, read_only=True)
class Meta:
model = ListOrder
fields = '__all__'
class ListsSerializer(serializers.ModelSerializer):
list_order = ListOrderSerializer(many=True, read_only=True)
class Meta:
model = Lists
fields = '__all__'
views.py
class ListsViewSet(generics.ListCreateAPIView):
"""
API endpoint that returns a list with its meta-information
"""
queryset = Lists.objects.all()
serializer_class = ListsSerializer
def get_queryset(self):
list_id = self.kwargs['list_id']
filters = [Q(id=list_id)]
return Lists.objects.filter(*filters)
def list(self, request, list_id):
queryset = self.get_queryset()
list_serializer = ListsSerializer(queryset, many=True)
return Response({ 'lists': list_serializer.data })
I'm pretty new to Django and like what it offers so far, though maybe I'm thinking of doing this in too much of a "SQL" way. I've read about select_related() and prefetch_related(), but not sure how I would apply it to this case. Any assistance is greatly appreciated and let me know if there's any other information I can provide.
In your ListOrderSerializer you are trying to serialize item. while in ListOrder model you used the field name item_id
Solution:
In ListOrderSerializer:
class ListOrderSerializer(serializers.ModelSerializer):
item_id = ItemsSerializer(many=False, read_only=True)
...

How can i Get foreign key's value instead of id , in django rest framework

Sorry for my bad English hope u can understand what i mean.
OUT PUT IM GETTING NOW:
[
{
"MainCatName": 1,
"Name": "harry potter",
"Image": "/media/101029496--sites-default-files-images-101029496-3176173-1748009911-hp.jp-1_MoxqrLp.jpg"
},
{
"MainCatName": 2,
"Name": "Princes Girl",
"Image": "/media/character_princess_rapunzel_8320d57a.jpeg"
},
{
"MainCatName": 3,
"Name": "sex in the city",
"Image": "/media/250px-SATC_Title.jpg"
},
{
"MainCatName": 4,
"Name": "who is dragon",
"Image": "/media/Reggio_calabria_museo_nazionale_mosaico_da_kaulon.jpg"
},
{
"MainCatName": 2,
"Name": "drama queen",
"Image": "/media/15241421_170761763390015_7913498865987146084_n.jpg"
}
]
WHAT I WANT :
I WANT TO TO RETURN THE FORIGN KEY'S (MainCatName) VALUE WHICH IS IN TABLE INSTEAD OF ID. ie value in the [CategoryName = models.CharField(max_length=50)] in my models.py
LIKE
[
{
"MainCatName": Story Books,
"Name": "harry potter",
"Image": "/media/101029496--sites-default-files-images-101029496-3176173-1748009911-hp.jp-1_MoxqrLp.jpg"
},
{
"MainCatName": Darama,
"Name": "Princes Girl",
"Image": "/media/character_princess_rapunzel_8320d57a.jpeg"
},
{
"MainCatName": Roamance,
"Name": "sex in the city",
"Image": "/media/250px-SATC_Title.jpg"
},
{
"MainCatName": sex,
"Name": "who is dragon",
"Image": "/media/Reggio_calabria_museo_nazionale_mosaico_da_kaulon.jpg"
},
{
"MainCatName": darama,
"Name": "drama queen",
"Image": "/media/15241421_170761763390015_7913498865987146084_n.jpg"
}
]
Here is my code :
veiws.py :
class GETCATVeiw(APIView):
def get(self, request):
data = Products.objects.only('MainCatName','Name','Image')
serializer = GETCAT(data, many=True)
return Response(serializer.data)
def post(self):
pass
models.py :
class Category(models.Model):
CategoryName = models.CharField(max_length=50)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.CategoryName
class Products(models.Model):
MainCatName = models.ForeignKey(Category, on_delete=models.CASCADE)
Name = models.CharField(max_length=50)
Image = models.ImageField()
Price = models.IntegerField()
DiscriptionHeading = models.CharField(max_length=100)
DiscriptionParagraph = models.TextField(max_length=1000)
class Meta:
verbose_name_plural = 'Products'
def __str__(self):
return str(self.MainCatName)+' - '+self.Name+' - '+str(self.Price)+' $'
serializers.py :
class GETCAT(serializers.ModelSerializer):
class Meta:
model = Products
fields = ('MainCatName', 'Name', 'Image')
You have the SlugRelatedField for that:
class GETCAT(serializers.ModelSerializer):
MainCatName = serializers.SlugRelatedField(read_only=True, slug_field='CategoryName')
class Meta:
model = Products
fields = ('MainCatName', 'Name', 'Image')
Edit:
I'm not sure it'll work with Products.objects.only('MainCatName','Name','Image') as you'll span a database relation. Also you'll likely want to use a select_related to avoid getting N+1 DB query.
The generic Django way is to define natural keys on your model, in this case Category:
Serializing by natural key: Add a natural_key(self) method to your Category class in which you return the category's CategoryName. It has to be unique!
def natural_key(self):
return self.CategoryName
Deserializing by natural key: You want to define a default manager for your Category model:
objects = CategoryManager()
and define the get_by_natural_key(self, name) method in your CategoryManager(models.Manager) class, which returns the category:
class CategoryManager(models.Manager):
def get_by_natural_key(self, name):
return self.get(CategoryName=name)