Related
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',
# }
models.py
class Product(models.Model):
product_id = models.AutoField(unique=True, primary_key=True)
product_name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = "product_master"
def __str__(self):
return self.product_name
class Organisation(models.Model):
"""
Organisation model
"""
org_id = models.AutoField(unique=True, primary_key=True)
org_name = models.CharField(max_length=100)
org_code = models.CharField(max_length=20)
org_mail_id = models.EmailField(max_length=100)
org_phone_number = models.CharField(max_length=20)
org_address = models.JSONField(max_length=500, null=True)
product = models.ManyToManyField(Product, related_name='products')
org_logo = models.ImageField(upload_to='org_logo/')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = "organisation_master"
def __str__(self):
return self.org_name
serializers.py
class Product_Serializers(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('product_id', 'product_name',)
class Organisation_Serializers(serializers.ModelSerializer):
product = Product_Serializers(many=True)
class Meta:
model = Organisation
fields = ('org_id', 'org_name', 'org_address', 'org_phone_number', 'org_mail_id','org_logo','org_code','product')
depth = 1
"
While i tried to do POST method for the organisation model I have tried giving the input for product as "product: 5" and "product: {"product_id": 5,"product_name": "time"} in the postman form data but it is showing as
{
"status": "error",
"code": 400,
"data": {
"product": [
"This field is required."
]
},
"message": "success"
}
Views.py
class Organisation_Viewset(DestroyWithPayloadMixin,viewsets.ModelViewSet):
renderer_classes = (CustomRenderer, ) #ModelViewSet Provides the list, create, retrieve, update, destroy actions.
queryset=models.Organisation.objects.all()
parser_classes = [MultiPartParser, FormParser]
serializer_class=serializers.Organisation_Serializers
def create(self, request, *args, **kwargs):
data = request.data
new_organisation= models.Organisation.objects.create(org_name=data["org_name"],org_code = ["org_code"], org_mail_id =data["org_mail_id"],org_phone_number= data["org_phone_number"], org_address=data["org_address"],org_logo = data["org_logo"])
new_organisation.save()
for product in data["product"]:
product_id = models.Product.objects.get(product_id=product["product_id"])
new_organisation.products.add(product_id)
serializer = serializers.Organisation_serializers(new_organisation)
return Response(serializer.data)
I need to post like this product: {"product_id": 5,"product_name": "time"}, what fields are available in the product model it should be posted on this product field.
Can you please suggest me a way as i tried many ways as per my knowledge but it dosen't worked.
you are using a tuple for fields, put a comma behind product in youre fields. If you use a list then dont use an end comma
fields = ('org_id', 'org_name', 'org_address', 'org_phone_number', 'org_mail_id','org_logo','org_code','product',)
depth = 1
Update your serializers to:
class Product_Serializers(serializers.Serializer):
product_id = serializers.IntegerField()
product_name = serializers.CharField(max_length=100)
class Organisation_Serializers(serializers.ModelSerializer):
product = Product_Serializers(many=True)
class Meta:
model = Organisation
fields = (
'org_id',
'org_name',
'org_address',
'org_phone_number',
'org_mail_id',
'org_logo',
'org_code',
'product'
)
depth = 1
Update your views as:
class Organisation_Viewset(ModelViewSet):
# ModelViewSet Provides the list, create, retrieve, update, destroy actions.
renderer_classes = (CustomRenderer,)
queryset = Organisation.objects.all()
parser_classes = [MultiPartParser, FormParser, JSONParser]
serializer_class = Organisation_Serializers
def create(self, request, *args, **kwargs):
serializer = Organisation_Serializers(data=request.data)
serializer.is_valid(raise_exception=True)
product_data = serializer.validated_data.pop('product')
does_not_exist = []
product_instances = []
for product in product_data:
try:
product_instance = Product.objects.get(
product_id=product['product_id'],
product_name=product['product_name']
)
product_instances.append(product_instance)
except Product.DoesNotExist:
does_not_exist.append(product)
if len(does_not_exist) > 0:
return Response({
'error': 'Product does not exist',
'does_not_exist': does_not_exist
}, status=400)
organization = Organisation.objects.create(**serializer.validated_data)
for product in product_instances:
organization.product.add(product)
organization.save()
return Response(Organisation_Serializers(organization).data, status=201)
Now we can send the list of product objects for the create API:
curl --location --request POST 'http://localhost:8000/api/organization/' \
--header 'Content-Type: application/json' \
--data-raw '{
"org_id": "12345",
"org_name": "test organization",
"org_address": "test",
"org_phone_number": "12345",
"org_mail_id": "test#te.st",
"org_code": "12345",
"product": [
{
"product_id": 1,
"product_name": "test p1"
},
{
"product_id": 2,
"product_name": "test p2"
}
]
}'
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
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
I got events that happen at locations:
class Event(models.Model):
title = models.CharField(max_length=200)
date_published = models.DateTimeField('published date',default=datetime.now, blank=True)
date_start = models.DateTimeField('start date')
date_end = models.DateTimeField('end date')
def __unicode__(self):
return self.title
description = models.TextField()
price = models.IntegerField(null=True, blank=True)
tags = TaggableManager()
location = models.ForeignKey(Location, blank=False)
class Location(models.Model):
location_title = models.CharField(max_length=200)
location_date_published = models.DateTimeField('published date',default=datetime.now, blank=True)
location_latitude = models.CharField(max_length=200)
location_longitude = models.CharField(max_length=200)
location_address = models.CharField(max_length=200)
location_city = models.CharField(max_length=200)
location_zipcode = models.CharField(max_length=200)
location_state = models.CharField(max_length=200)
location_country = models.CharField(max_length=200)
location_description = models.TextField()
def __unicode__(self):
return u'%s' % (self.location_title)
I can get the results of all via:
class EventSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.Field()
class Meta:
model = Event
depth = 2
fields = ('url','id','title','date_start','date_end','description', 'price', 'location')
Which outputs:
{
"url": "http://localhost:8000/api/event/3/",
"id": 3,
"title": "Testing",
"date_start": "2013-03-10T20:19:00Z",
"date_end": "2013-03-10T20:19:00Z",
"description": "fgdgdfg",
"price": 10,
"location": {
"id": 2,
"location_title": "Mighty",
"location_date_published": "2013-03-10T20:16:00Z",
"location_latitude": "37.767475",
"location_longitude": "-122.406878",
"location_address": "119 Utah St, San Francisco, CA 94103, USA",
"location_city": "San Francisco",
"location_zipcode": "94103",
"location_state": "California",
"location_country": "United States",
"location_description": "Some place"
}
},
However, I don't want it to grab all fields, as I don't need all of them. How can I define what fields should be retrieved from my nested object? Thanks!
Serializers can be nested, so do something like this...
class LocationSerializer(serializers.ModelSerializer):
class Meta:
model = Location
fields = (...)
class EventSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.Field()
location = LocationSerializer()
class Meta:
model = Event
fields = ('url','id','title','date_start','date_end','description', 'price', 'location')
I have been to this and did not get a perfect solution, But I did something you may check for it.
This method will not create nested serializers
**class LocationSerializer(serializers.ModelSerializer):**
class Meta:
model = Location
fields = (...) #does not matter
exclude = (...) #does not matter
class EventSerializer(serializers.ModelSerializer):**
loc_field_1 = serializers.CharField(required=False,*source='location.loc_field_1'*)
loc_field_2 = serializers.CharField(required=False,*source='location.loc_field_2'*)
***#ADD YOUR DESIRE FIELD YOU WANT TO ACCESS FROM OTHER SERIALIZERS***
class Meta:
model = Event
fields =('url','id','title','date_start','date_end','description', 'price', 'location')
I found this question when I was trying to figure out how to exclude certain fields from a serializer only when it was being nested. Looks like Tasawer Nawaz had that question as well. You can do that by overriding get_field_names. Here's an example based on Tom Christie's answer:
class LocationSerializer(serializers.ModelSerializer):
class Meta:
model = Location
fields = (...)
exclude_when_nested = {'location_title', 'location_date_published'} # not an official DRF meta attribute ...
def get_field_names(self, *args, **kwargs):
field_names = super(LinkUserSerializer, self).get_field_names(*args, **kwargs)
if self.parent:
field_names = [i for i in field_names if i not in self.Meta.exclude_when_nested]
return field_names
class EventSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.Field()
location = LocationSerializer()
class Meta:
model = Event
fields = ('url','id','title','date_start','date_end','description', 'price', 'location')