DRF post nested object - django

I`m trying to post this json object
{
"name": "Country Name",
"description": "Description here...",
"generalInfo": {
"capital": "Capital",
"population": "Population",
"highestPeak": "HighestPeak",
"area": "Area"
},
"timelineData": [
{
"name": "Name 1",
"ruler": "Ruler",
"dateStart": "dateStart",
"dateEnd": "dateEnd",
"description": "description"
},
{
"name": "Name 2",
"ruler": "Ruler",
"dateStart": "dateStart",
"dateEnd": "dateEnd",
"description": "description"
},
{
"name": "Name 3",
"ruler": "Ruler",
"dateStart": "dateStart",
"dateEnd": "dateEnd",
"description": "description"
}
]
}
But I`m receiving this error
ValueError at /countries/
Cannot assign "OrderedDict([('capital', 'Capital'), ('population', 'Population'), ('highestPeak', 'HighestPeak'), ('area', 'Area')])": "Country.generalInfo" must be a "GeneralInfo" instance.
Request Method: POST
Request URL: http://127.0.0.1:8000/countries/
Django Version: 4.0.3
Here are my models.py:
class GeneralInfo(models.Model):
capital = models.CharField(max_length=200)
population = models.CharField(max_length=200)
highestPeak = models.CharField(max_length=200)
area = models.CharField(max_length=200)
class TimelineData(models.Model):
name = models.CharField(max_length=200)
ruler = models.CharField(max_length=200)
dateStart = models.CharField(max_length=200)
dateEnd = models.CharField(max_length=200)
description = models.TextField(default='Description here...')
class Country(models.Model):
name = models.CharField(max_length=200, db_index=True)
description = models.TextField(default='Description here...')
generalInfo = models.ForeignKey(GeneralInfo, on_delete=models.CASCADE)
timelineData = models.ManyToManyField(TimelineData)
I have also overridden the create method for CountrySerializer and here are
my serializers.py:
class TimelineDataSerializer(serializers.ModelSerializer):
class Meta:
model = TimelineData
fields= '__all__'
class GeneralInfoSerializer(serializers.ModelSerializer):
class Meta:
model= GeneralInfo
fields = '__all__'
class CountrySerializer(serializers.ModelSerializer):
timelineData = TimelineDataSerializer(many=True)
generalInfo = GeneralInfoSerializer()
class Meta:
model= Country
fields = '__all__'
def create(self, validated_data):
timelineData = validated_data.pop('timelineData')
country = Country.objects.create(**validated_data)
for timelineItem in timelineData:
TimelineData.objects.create(country=country,**timelineItem)
return country
and views.py
class CountryList(viewsets.ModelViewSet):
serializer_class = CountrySerializer
queryset = Country.objects.all()
I am new to Django and Python so maybe I missed something. I've been stuck on this for a while now and wasn't able to find any solution myself

The error is because that OrderedDict needs to be an actual GeneralInfo Object..
I have not used serializers much (or at all tbh), but this would be my general guess from what I've seen from similar problems:
class CountrySerializer(serializers.ModelSerializer):
timelineData = TimelineDataSerializer(many=True)
generalInfo = GeneralInfoSerializer()
class Meta:
model= Country
fields = '__all__'
def create(self, validated_data):
timelineData = validated_data.pop('timelineData')
# pop out data (so it's not in final create), filter for object
generalInfoObj = GeneralInfo.objects.filter(**validated_data.pop('generalInfo')).first()
# ^ get or None (no crash, my preference)
# could also be:
# generalInfoObj = GeneralInfo.objects.get( ... )
# ^ - get or crash
# generalInfoObj, created = GeneralInfo.objects.get_or_create( ... )
# ^ get or create it
# debugger
print('generalInfoObj', type(generalInfoObj), generalInfoObj)
country = Country.objects.create(**validated_data, GeneralInfo=generalInfoObj)
for timelineItem in timelineData:
TimelineData.objects.create(country=country,**timelineItem)
return country
If generalInfo shows up like an array of tuples, you'd do something like this with List Comprehension
(adding this just in case)
generalInfoObj = GeneralInfo.objects.filter(**{i[0]:i[1] for i in validated_data.pop('generalInfo')}).first()
## breakdown:
# generalinfo = [('key0', val0'), ('key1', val1')]
dict = {}
for i in generalinfo:
# i = ('key', 'value')
dict[i[0]] = i[1]
# dict = {
# 'key0': 'val0',
# 'key1': 'val1',
# }

Related

Django Rest Framwork : three level relationship with nested serializer

I have double nested serializer situation...
i have three models : Reports, ReportPages and widgets , upon trying to create a specific endpoind that is :
payload {
"since_date": "some date",
"until_date": "some other date that is greater than since_date",
"report_pages": [
{
"page_number": "some number" (generated from front end, of type integer)
"widgets": [
{
"data": ["some array"],
"width": "some number",
"height": "some number",
"top_position": "some number",
"left_position": "some number",
"widget_type": "" (either "Label", "LineChart", "Bar" or "PieChart"),
}
]
}
]
}
I faced a problem with nested serializer, i was only able to create the first half which is :
payload {
"since_date": "some date",
"until_date": "some other date that is greater than since_date",
"report_pages": [
{
"page_number": "some number" (generated from front end, of type integer)
]
}
Knowing that the creation of a report is separate from this endpoint, so i override the update method and structure looks like this :
Report{
report_page{
widgets {}
}
}
My models :
class Reports(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=500, null=True)
since_date = models.DateTimeField(null=True)
until_date = models.DateTimeField(null=True)
def __str__(self):
return self.name
class ReportPages(models.Model):
id = models.AutoField(primary_key=True)
number = models.IntegerField(null=True)
report = models.ForeignKey(Reports, related_name="report_pages",on_delete=models.CASCADE)
def __str__(self):
return self.number
class Widgets(models.Model):
id = models.AutoField(primary_key=True, unique=True)
data = ArrayField(models.CharField(max_length=10, blank=True))
width = models.IntegerField(null=True)
height = models.IntegerField(null=True)
top_position = models.FloatField(null=True)
left_position = models.FloatField(null=True)
widget_type = models.CharField(max_length=500, null=True)
report_pages = models.ForeignKey(ReportPages,related_name="widgets", on_delete=models.CASCADE)
My views :
class Reports2ViewSet(viewsets.ModelViewSet):
queryset = Reports.objects.all()
serializer_class = Reports2Serializer
http_method_names = ['get','post','retrieve','put','patch']
My serializers :
class ReportWidgetsSerializer(serializers.ModelSerializer):
class Meta:
model = Widgets
fields = '__all__'
class ReportPagesSerializer(serializers.ModelSerializer):
widgets = ReportWidgetsSerializer(many=True)
class Meta:
model = ReportPages
fields = ('number','widgets',)
def update(self, instance, validated_data):
return super().update(instance, validated_data)
class Reports2Serializer(serializers.ModelSerializer):
report_pages = ReportPagesSerializer(many=True)
class Meta:
model = Reports
fields = ('report_pages','since_date','until_date',)
def update(self, instance, validated_data):
page_list = validated_data.pop('report_pages')
instance.since_date = validated_data.get('since_date', instance.since_date)
instance.until_date = validated_data.get('until_date', instance.until_date)
instance.save()
serializer = self.fields['report_pages']
widget_list = []
for page in page_list:
page['report'] = instance
widget_list = page.pop('widgets')
page = ReportPages.objects.create(**page)
print(page)
for widget in widget_list:
print('the widget is', widget)
widget['report_pages'] = page
widget = Widgets.objects.create(**widget)
print(widget)
# pages = serializer.create(page_list)
return instance
the error is :
{
"report_pages": [
{
"widgets": [
{
"report_pages": [
"This field is required."
]
}
]
},
{
"widgets": [
{
"report_pages": [
"This field is required."
]
}
]
}
]
}

how to serialize properly in perticular format

Please help me improve my code.. im newly placed and learning things
i want to display separately product field in my code just like category and sub-category in my code but i don't know how to. Also please point out if there anything that i can improve in my code
Here is my output:
[
{
"Category": {
"c_id": 1,
"parent_id": 15
},
"Sub_category": {
"sc_id": 1,
"is_active2": 1
},
"p_id": 2,
"group_id": "",
"product_name": "Fruit",
"is_prescription_needed": 1,
}]
Whereas my expected format is:
[
{
"Category": {
"c_id": 1,
"parent_id": 15
},
"Sub_category": {
"sc_id": 1,
"is_active2": 1
},
"Product": { # How do i separate this "Product" key and value
"p_id": 2,
"group_id": "",
"product_name": "Fruit",
"is_prescription_needed": 1
}]
here is my code:
Serialziers.py
class ProductCategorySerializer(serializers.ModelSerializer):
c_id = serializers.SerializerMethodField()
category_image = serializers.SerializerMethodField()
class Meta:
fields = ["c_id","parent_id","category","category_image","is_active"]
model = Category
def get_c_id(self,obj):
return obj.id
def get_category_image(self,obj):
return obj.image
class ProductSubCategorySerializer(serializers.ModelSerializer):
sc_id = serializers.SerializerMethodField()
is_active2 = serializers.SerializerMethodField()
class Meta:
fields = ["sc_id","sub_category","is_active2"]
model = ProductSubCategory
def get_sc_id(self,obj):
return obj.id
def get_is_active2(self,obj):
return obj.is_active
class ProductSerializer(serializers.ModelSerializer):
Category = ProductCategorySerializer(source='category', read_only=True)
Sub_category = ProductSubCategorySerializer(source='sub_category', read_only=True)
product_image = serializers.SerializerMethodField()
p_id = serializers.SerializerMethodField()
class Meta:
fields = ["Category","Sub_category", "p_id", "group_id", "product_name", "is_prescription_needed",
"product_description", "product_keypoint", "product_type", "product_image", "pack", "pack_unit", "hsn",
"company", "brand_id", "composition", "drug_details", "uses",
"side_effects", "cope_side_effects", "how_it_works", "safety_advice", "what_if_forget",
"medical_constraints", "created_at", "created_by", "modified_at", "modified_by"]
model = Product
def get_product_image(self, obj: Product):
image = obj.image
if len(image) == 0:
return ""
return image
def get_p_id(self,obj):
return obj.id
View.py
class categoryProduct(generics.GenericAPIView):
def post(self, request):
params = request.query_params
category = params.get("category")
sub_category = params.get("sub_category")
kwargs = {}
if category:
kwargs['category'] = category
else:
kwargs['category'] = 1
if sub_category:
kwargs['sub_category'] = sub_category
else:
kwargs['sub_category'] = 1
queryset = Product.objects.filter(**kwargs)
all_response = ProductSerializer(queryset, many=True)
#AFTER PAGINATION
return Response(all_response)
models.py
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, db_column='category')
sub_category = models.ForeignKey(ProductSubCategory, on_delete=models.CASCADE, db_column='sub_category')
group_id = models.CharField(max_length=200)
product_name = models.CharField(max_length=500)
is_prescription_needed = models.IntegerField()
...
class Meta:
managed = False
db_table = 'product'
class Category(models.Model):
category = models.CharField(max_length=50)
image = models.CharField(max_length=500)
store_image = models.CharField(max_length=1000)
is_active = models.IntegerField()
parent_id = models.IntegerField()
class Meta:
managed = False
db_table = 'category'
class ProductSubCategory(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, db_column="category")
sub_category = models.CharField(max_length=250)
is_active = models.IntegerField()
class Meta:
managed = False
db_table = 'sub_category'
You can make a separate field that is a a SerializerField of a serializer you create with the proper fields you want displayed under the Product key. This is how your other field, like Category get appropriately created. That is to say: remove the fields from the ProductSerializer into a new serializer, then add a Product SerializerField with the new serializer containing those keys.
One other way you can do this is to create a .to_representation method to change the output to precisely the format you want.
class ProductSerializer(serializers.ModelSerializer):
...
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['Product'] = {
"p_id": representation.pop('p_id')
"group_id": representation.pop('group_id'),
"product_name": representation.pop('product_name'),
"is_prescription_needed": representation.pop('is_prescription_needed')
}
return representation

Python Django Display Nested Objects

Hi I'm new to django rest framework and I'm trying to serialize 3 nested models. The relationships are:
hotel_social_media_type has a one to many relationship to hotel_social_media
and hotel has one to many relationship to hotel_social_media. Right now I can only serialized hotel to hotel_social_media but I can't serialize hotel_social_media_type.
Here's my serializers:
class SocialMediaTypeSerializer(serializers.ModelSerializer):
"""Serializes social media type"""
class Meta:
model = models.SocialMediaType
fields = ('name', 'icon', 'url')
class HotelSocialMediaSerializer(serializers.ModelSerializer):
"""Serializes media files"""
hotel_social_media_type = SocialMediaTypeSerializer(many=False, read_only=True)
class Meta:
model = models.HotelSocialMedia
fields = ('url', 'hotel_social_media_type')
class HotelSerializer(serializers.ModelSerializer):
"""Serializes Restaurat, Bars, TouristInformation and Tourist Spots """
hotel_images = HotelImageSerializer(many=True, read_only=True)
hotel_social_media = HotelSocialMediaSerializer(many=True, read_only=True)
class Meta:
model = models.Hotel
fields = ('id', 'name', 'hotel_images', 'hotel_social_media')
Models:
class Hotel(models.Model):
"""Database model for hotels"""
name = models.CharField(max_length=50)
def __str__(self):
"""Return the model as a string"""
return self.name
class HotelImage(models.Model):
"""Image upload for hotel"""
name = models.CharField(max_length=50)
description = models.CharField(max_length=250)
path = models.ImageField(upload_to='images/')
created_on = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
hotel = models.ForeignKey(Hotel, related_name='hotel_images', on_delete=models.CASCADE, null=True)
def __str__(self):
"""Return the model as a string"""
return self.name
class SocialMediaType(models.Model):
"""Social Media Type eg: fb, twitter"""
name = models.CharField(max_length=50)
icon = models.ImageField(upload_to='images/')
url = models.CharField(max_length=50)
def __str__(self):
"""Return the model as a string"""
return self.name
class HotelSocialMedia(models.Model):
"""Social Media"""
hotel = models.ForeignKey(Hotel, related_name='hotel_social_media', on_delete=models.CASCADE, null=True)
type = models.ForeignKey(SocialMediaType, related_name='hotel_social_media_type', on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=50)
url = models.CharField(max_length=50)
def __str__(self):
"""Return the model as a string"""
return self.name
Current result is:
{
"id": 1,
"name": "Alta Vista",
"hotel_images": [
{
"id": 1,
"name": "Alta Vista",
"path": "http://127.0.0.1:8000/media/images/hotel-1.jpg"
}
],
"hotel_social_media": [
{
"url": "https://www.facebook.com/abscbnNEWS"
}
]
}
What I want is:
{
"id": 1,
"name": "Alta Vista",
"hotel_images": [
{
"id": 1,
"name": "Alta Vista",
"path": "http://127.0.0.1:8000/media/images/hotel-1.jpg"
}
],
"hotel_social_media": [
{
"url": "https://www.facebook.com/abscbnNEWS",
"hotel_social_media_type": {
"name": "Facebook",
"icon": "http://127.0.0.1:8000/media/images/fb-1.jpg"
}
}
]
}
`hotel_social_media` must also display `hotel_social_media_type`
Based on your model, you must use type keyword instead of hotel_social_media_type in your HotelSocialMediaSerializer. Because your HotelSocialMedia has type field for relation with HotelSocialMediaType.To change keyword can solve your problem.
class HotelSocialMediaSerializer(serializers.ModelSerializer):
"""Serializes media files"""
type = SocialMediaTypeSerializer(many=False, read_only=True)
class Meta:
model = models.HotelSocialMedia
fields = ('url', 'type')
If you want to use hotel_social_media_type keyword, you can use SerializerMethodField like that:
class HotelSocialMediaSerializer(serializers.ModelSerializer):
"""Serializes media files"""
hotel_social_media_type = serializers.SerializerMethodField()
class Meta:
model = models.HotelSocialMedia
fields = ('url', 'hotel_social_media_type')
def get_hotel_social_media_type(self.obj):
serializer = SocialMediaTypeSerializer(obj.type)
return serializer.data

Django in one serialize pull out child objects

This my models
class Dictionary(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
parentId = models.UUIDField(editable=True, null=True)
name = models.CharField(max_length=100)
date_create = models.DateTimeField(auto_now=True)
date_end = models.DateTimeField(auto_now=False, null=True)
class Teacher(models.Model):
name = models.CharField(max_length=100)
message = models.CharField(max_length=300)
status = models.OneToOneField(Dictionary, on_delete=models.CASCADE)
this is my urls
from django.urls import path
from . import views
urlpatterns = [
path('get', views.GetViewSet.as_view({'get': 'list'})),
]
This is ViewSet
class GetViewSet(viewsets.ModelViewSet):
MyApiObj = null
#property
def api_object(self):
return namedtuple("ApiObject", self.request.data.keys())(*self.request.data.values())
def get_serializer_class(self):
GeneralSerializer.Meta.model = apps.get_model(app_label=self.MyApiObj.app, model_name=self.MyApiObj.object)
return GeneralSerializer
def post(self, request):
self.MyApiObj = self.api_object
return self.select_api()
def select_api(self):
queryset = QueryHelper.select(self.MyApiObj)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Serializer
class GeneralSerializer(serializers.ModelSerializer):
class Meta:
model = None
fields = '__all__'
My post parameters to django
{
"app":"leads",
"object":"Teacher",
"settings":{
},
"data":{
}
}
answer:
[
{
"id": 1,
"name": "John",
"message": "Hi everyone",
"status": "e3b86ed4-8794-413b-994c-b1ec0a43eebe"
}
]
Problem is Dictionary(status) model give me id(uuid) but i need whole object without creating new serializer for Dictionary. i do univeral serializer for all models in my app
Try this:
class DictionarySerializer(serializers.ModelSerializer):
class Meta:
model = Dictionary
fields = '__all__'
class GeneralSerializer(serializers.ModelSerializer):
status = DictionarySerializer(required=True)
class Meta:
model = None
fields = '__all__'
But it is not good for me because 1) Without other serializer 2) I need Universal serializer for all models and with child model in all models of my project. Help me please)
I need something like this
[
{
"id": 1,
"status": {
"id": "e3b86ed4-8794-413b-994c-b1ec0a43eebe",
"parentId": "dc6cf7da-b82c-11e9-a2a3-2a2ae2dbcce4",
"name": "Spravochnik1",
"date_create": "2019-08-06T09:30:49.355439Z",
"date_end": "2019-08-06T09:29:57Z"
},
"name": "John",
"message": "Hi everyone"
}
]
for nested serialization check full ref here
and for your case add depth = 1
class GeneralSerializer(serializers.ModelSerializer):
status = DictionarySerializer(required=True)
class Meta:
model = None
fields = '__all__'
depth = 1

Connect nested models django rest framework API serializer

I want to achieve this: a API to get all products for a category.
Models.py
class Category(models.Model):
main_category = models.CharField(max_length=200, default = '')
def __str__(self):
return '%s' % (self.main_category)
class Subcategory(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
sub_category = models.CharField(max_length=200, default = '')
def __str__(self):
return '%s' % (self.sub_category)
class Products(models.Model):
sub_category = models.ForeignKey(Subcategory, on_delete=models.CASCADE)
product_name = models.CharField(max_length=200, default = '')
def __str__(self):
return '%s' % (self.product_name)
Views.py
class ProductCategoryFilter(django_filters.FilterSet):
category = django_filters.CharFilter(name="category__main_category")
class Meta:
model = Products
fields = ['category']
class ProductCategoryListAPIView(ListAPIView):
queryset = Products.objects.all()
serializer_class = ProductCategorySerializer
filter_class = ProductCategoryFilter
Serializers.py
class ProductCategorySerializer(ModelSerializer):
#category_name = serializers.CharField(source='category.main_category')
category_name = CategorySerializer(source='category_set')
class Meta:
model = Products
fields = [
'category_name',
'product_name',
]
AttributeError at /api/productcategories/
I get AttributeError when attempting to get a value for field category_name on serializer ProductCategorySerializer. The serializer field might be named incorrectly and not match any attribute or key on the Products instance.
Original exception text was: 'Products' object has no attribute 'category'.
And I want the output like this:
[
{
"category_name": "main category 1",
"product_name": "product 1"
},
{
"category_name": "main category 1",
"product_name": "product 2"
},..........
OR output like this :
[
{
"main category 1" :{ "product 1", "product 2"...}