I am creating an API using Django Restframework which needs data from multiple models. I got many answers for my requirement but it isn't working.
I have my models as follows
class Task(models.Model):
title = models.CharField(max_length=200)
completed = models.BooleanField(default=False, blank=True, null=True)
def __str__(self):
return self.title
class Task_extended(models.Model):
task_id = models.ForeignKey(Task, on_delete = models.CASCADE,related_name='task_extendeds')
field_3 = models.CharField(max_length=200)
field_5 = models.CharField(max_length=200)
field_4 = models.BooleanField(default=False, blank=True, null=True)
def __str__(self):
return self.field_3
Here's my view function
#api_view(['GET','POST'])
def taskList(request):
tasks = Task.objects.all()
serializer = TaskSerializer(tasks, many =True)
return Response(serializer.data)
Serializer.py
class TaskSerializer(serializers.ModelSerializer):
task_extendeds = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Task
fields = "__all__"
depth = 1
I am getting the json as following
[
{
"id": 2,
"task_extendeds": [
1,
2,
3
],
"title": "Start Rest Framework",
"completed": false
}
]
What changes should I do to Serializers.py so that my json output is as following
[
{
"id": 2,
"title": "Start Rest Framework",
"completed": false,
"task_extendeds": [
{
"field_3": "Field 3 Data",
"field_4": "Field 4 Data",
"field_5": "Field 5 Data"
},
{
"field_3": "Field 3 Data",
"field_4": "Field 4 Data",
"field_5": "Field 5 Data"
},
{
"field_3": "Field 3 Data",
"field_4": "Field 4 Data",
"field_5": "Field 5 Data"
}
],
}
]
The depth = 1 attribute in meta class should have got the work done according to other stackoverflow questions, but it isn't working.
You use a subserializer, so:
class Task_extendedSerializer(serializers.ModelSerializer):
class Meta:
model = Task_extended
fields = ['field_3', 'field_4', 'field_5']
class TaskSerializer(serializers.ModelSerializer):
task_extendeds = Task_extendedSerializer(many=True)
class Meta:
model = Task
fields = '__all__'
In the view you can boost efficiency by prefetching the task_extendeds:
#api_view(['GET'])
def taskList(request):
tasks = Task.objects.prefetch_related('task_extendeds')
serializer = TaskSerializer(tasks, many=True)
return Response(serializer.data)
Note: Models in Django are written in PascalCase, not snake_case,
so you might want to rename the model from Task_extended to TaskExtended.
Write a Task_extendedSerializer first then use it in TaskSerializer
class Task_extendedSerializer(serializers.ModelSerializer):
class Meta:
model = Task_extended
fields = ('field_3', 'field_4', 'field_5')
class TaskSerializer(serializers.ModelSerializer):
task_extendeds = Task_extendedSerializer()
class Meta:
model = Task
fields = ('id', 'title', 'completed', 'task_extendeds')
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',
# }
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
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
I have three models, three serializers, one modelviewset below.
I am using django-rest-framework to make a rest api for android.
The restaurant model was created first. Then I created a star model and an image model.
What I want to do is to add star and image objects into restaurant objects.
finally I've got what I want result but I think my viewset code looks like wrong..
Is there another way not to use "for loop"?
Models
class Restaurant(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
address = models.CharField(max_length=255)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
weather = models.ForeignKey(Weather, on_delete=models.CASCADE)
distance = models.ForeignKey(Distance, on_delete=models.CASCADE)
description = models.TextField('DESCRIPTION')
def __str__(self):
return self.name
class Star(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.IntegerField('RATING')
def __str__(self):
return self.restaurant
class RestaurantImage(models.Model):
id = models.AutoField(primary_key=True)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
path = models.CharField(max_length=255)
Serializer
class StarSerializer(serializers.ModelSerializer):
class Meta:
model = Star
fields = ('id', 'restaurant', 'user', 'rating', )
class RestaurantDetailSerializer(serializers.ModelSerializer):
category = CategorySerializer()
weather = WeatherSerializer()
distance = DistanceSerializer()
class Meta:
model = Restaurant
fields = ('id', 'name', 'address', 'category', 'weather',
'distance', 'description', )
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantImage
fields = ('id', 'path', 'restaurant')
ViewSet
class RestaurantDetailInfoViewSet(viewsets.ModelViewSet):
queryset = Restaurant.objects.all()
serializer_class = RestaurantSerializer
def list(self, request, *args, **kwargs):
restaurant_list = Restaurant.objects.all()
restaurant_result = []
for restaurant in restaurant_list:
restaurantInfo = Restaurant.objects.filter(id=restaurant.pk)
restaurant_serializer = RestaurantDetailSerializer(restaurantInfo, many=True)
ratingAverageValue = Star.objects.filter(restaurant=restaurant.pk).aggregate(Avg('rating'))
images = RestaurantImage.objects.filter(restaurant=restaurant.pk)
image_serializer = ImageSerializer(images, many=True)
restaurant_dic = {
'restaurant': restaurant_serializer.data,
'ratingAverage': ratingAverageValue['rating__avg']
if ratingAverageValue['rating__avg'] is not None else 0,
'images': image_serializer.data
}
restaurant_result.append(restaurant_dic)
return Response(restaurant_result)
Result
[
{
"restaurant": [
{
"id": 1,
"name": "restaurant1",
"address": "address1",
"category": {
"c_id": 1,
"name": "foodtype1"
},
"weather": {
"w_id": 1,
"name": "sunny"
},
"distance": {
"d_id": 1,
"name": "inside"
},
"description": "description1"
}
],
"ratingAverage": 2.6667,
"images": [
{
"id": 1,
"path": "imagepath",
"restaurant": 1
}
]
},
Solution:
class RestaurantDetailSerializer(serializers.ModelSerializer):
category = CategorySerializer()
weather = WeatherSerializer()
distance = DistanceSerializer()
images = ImageSerializer(many=True, read_only=True)
ratingAverage = serializers.SerializerMethodField(read_only=True)
def get_ratingAverage(self, restaurant):
ratingAvgVal = Star.objects.filter(
restaurant=restaurant
).aggregate(Avg('rating'))['rating__avg']
return ratingAvgVal if ratingAvgVal is not None else 0
class Meta:
model = Restaurant
fields = ('id', 'name', 'address', 'category', 'weather',
'distance', 'description', 'images', 'ratingAverage', )
Explanation:
Here, I have nested the ImageSerializer in the RestaurantSerializer class, since you needed all the fields you've defined in ImageSerializer.
Then, for ratingAverage, I have used the SerializerMethodField which returns the value calculated (your logic) in the method I've defined for it, i.e. get_ratingAverage, which takes the Restaurant instance reference passed as an argument to the method for the field.
Here are my models:
class Property(models.Model):
id = [...]
address = [...]
class Property_Value(models.Model):
id = models.ForeignKey(Property)
amount = [...]
last_updated = [...]
def __unicode__(self):
return '%s: %s' % (self.last_updated, self.amount)
My serializer are:
class Property_ValueSerializer(serializers.ModelSerializer):
class Meta:
model = Property_Value
fields = ('last_updated', 'amount')
class PropertySerializer(serializers.ModelSerializer):
1) property_values = Property_ValueSerializer(source='property_value_set', many=True)
2) property_values = serializers.RelatedField(many=True, read_only=True)
class Meta:
model = Property
fields = ('id', 'address', 'property_values')
I would like to display a json like this:
[
{
"id": "2",
"address": "123 Apple Lane",
"property_values": [
{
"10/13/2016": "1709195.00"
}
]
}
]
If I use option 1, I get:
[
{
"id": "2",
"address": "123 Apple Lane",
"property_values": [
{
"last_updated": "10/13/2016",
"amount": "1709195.00"
}
]
}
]
If I user option 2 (which is what was suggested by the Rest tutorial, I get an Attribution Error:
AttributeError at /api/property/
'Property' object has no attribute 'property_values'
I'm not quite sure what I am doing wrong. Can someone point out what I am doing wrong? Thanks.
You can use StringRelatedField which returns the __unicode__ representation for the related fields. In your case __unicode__ method already returns the value in the way you are expecting in serializer.
class PropertySerializer(serializers.ModelSerializer):
property_values = serializers.StringRelatedField(many=True)
class Meta:
model = Property
fields = ('id', 'address', 'property_values')
class Property_Value(models.Model):
id = models.ForeignKey(Property, related_filed="property_values")
amount = [...]
last_updated = [...]
def __unicode__(self):
return '%s: %s' % (self.last_updated, self.amount)