Model
class makitVerifications (models.Model):
Matter_fk = models.ForeignKey(Matter,on_delete=models.CASCADE , related_name = "makit_validate")
user_fk = models.ForeignKey('auth.User',on_delete=models.CASCADE , related_name = 'verification_by' , )
Serializer
class UserVerifyCountSerializer (serializers.ModelSerializer):
# Tried makit_validate = makitSerializer (read_only = True)
class Meta :
model = makitVerifications
fields = ( 'user_fk', 'makit_validate', 'Matter_fk')
UserVerifyCountSerializer._declared_fields['makit_validate']= makitSerializer (read_only = True)
View
class UserVerfiyCountViewSet(generics.ListCreateAPIView):
serializer_class = UserVerifyCountSerializer
def get_queryset(self):
queryset = makitVerifications.objects.all()
return queryset
JSON
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"user_fk": 1,
"Matter_fk": 1
},
{
"user_fk": 1,
"Matter_fk": 1
}
]
The field "makit_validate" which is expected as nested object is missing in the JSON .How can I get nested object of the related model in json .
I also tried an example from the docs but the same field is missing from JSON
Also how to omit/disable fields from the objected being nested in the above serializer class
Makie Serilazer
class makitSerializer(serializers.ModelSerializer):
kamaz = kamazSerializer(many=True,read_only=True)
facilities = FacilitiesSerializer (many =True,read_only=True)
class Meta :
model = Makit
fields = '__all__'
## Makit Model ##
class Makit (models.Model):
pincode = models.IntegerField()
area = models.CharField(max_length=200)
street = models.CharField(max_length=200)
landmark = models.CharField(max_length=100,blank=True , null = True)
contact_no= models.BigIntegerField()
From doc here: Serializer fields
Try:
makit_validate = makitSerializer(source="the_field_you_find_makit" read_only = True)
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',
# }
I need to get the value of the foreignkey field not as an id but as a value of one of the fields of the linked model.
models.py
class RfiParticipation(models.Model):
...
vendor = models.ForeignKey('Vendors', models.DO_NOTHING, related_name='to_vendor')
m = models.ForeignKey('Modules', models.DO_NOTHING, related_name='to_modules')
...
class Modules(models.Model):
MODULES_NAME = (....
)
mid = models.AutoField(primary_key=True)
module_name = models.CharField(max_length=50, choices=MODULES_NAME, unique=True)
serializer.py
class VendorsManagementListSerializer(serializers.ModelSerializer):
company_information = serializers.SerializerMethodField()
vendor_modules = serializers.SerializerMethodField()
class Meta:
model = Vendors
fields = ('vendor_name',
...
'company_information',
'vendor_modules',)
def get_vendor_modules(self, obj):
r = RfiParticipation.objects.filter(vendor=obj).order_by('rfi').values('m', 'rfi')
return r
Now this request r = RfiParticipation.objects.filter(vendor=obj).order_by('rfi').values('m', 'rfi') returns to me:
"vendor_modules": [
{
"m": 2,
"rfi": "20R1"
},
{
"m": 3,
"rfi": "20R1"
},
{
"m": 4,
"rfi": "20R1"
}
]
How I can make m: module_name instead of m: 2? Where module_name is the field from Modules model.
I try .values('m.module_name', 'rfi') but got
Cannot resolve keyword 'm.module_name' into field. Choices are: active, id, m, m_id, rfi, rfi_id, timestamp, user_id, vendor, vendor_id
You can use SlugRelatedField here. For example:
class RFISerializer(serializers.ModelSerializer):
to_modules = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='module_name'
) # using to_modules as it was used as related field
class Meta:
model = RfiParticipation
fields = ['rfi', 'to_modules']
class VendorsManagementListSerializer(serializers.ModelSerializer):
vendor_modules = RFISerializer(source='m')
class Meta:
model = Vendors
fields = ('vendor_name',
...
'vendor_modules',)
Nice decision )
r = RfiParticipation.objects.filter(vendor=obj).order_by('rfi').values(module=F('m__module_name'), round=F('rfi'))
My models:
class ContentHotel(models.Model):
hotel_id = models.IntegerField(unique=True, blank=True, primary_key=True)
class Meta:
managed = False
db_table = 'content_hotels'
ordering = ('hotel_id',)
def __str__(self):
return str(self.hotel_id)
class RateHotel(models.Model):
rate_hotel_id = models.IntegerField(blank=True, primary_key=True, unique=True)
content_hotel = models.ForeignKey(ContentHotel, on_delete=models.CASCADE, related_name='rate_hotel')
class Meta:
managed = False
db_table = 'rate_hotels'
ordering = ('rate_hotel_id',)
def __str__(self):
return str(self.rate_hotel_id)
My Serializers:
class RateHotelSerializer(serializers.ModelSerializer):
class Meta:
model = RateHotel
fields = __all__
class ContentHotelSerializer(serializers.ModelSerializer):
rate_hotel = RateHotelSerializer(many=True)
class Meta:
model = ContentHotel
fields = ('hotel_id', 'rate_hotel')
def create(self, validated_data):
rate_hotels = validated_data.pop('rate_hotel')
content_hotel = ContentHotel.objects.create(**validated_data)
for rate_hotel in rate_hotels:
RateHotel.objects.create(content_hotel=content_hotel, **rate_hotel)
return content_hotel
JSON:
{
"hotel_id": -1,
"rate_hotel": [{"content_hotel": -1, "rate_hotel_id": 1}]
}
Above JSON input gives me error like:
{
"rate_hotel": [
{
"content_hotel": [
"Invalid pk \"1\" - object does not exist."
]
}
],
"status_code": 400
}
REFERENCE: http://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers
I referenced the link above, anyone knows how to address this? But f I create the two objects separately, it works correctly, like this:
{
"hotel_id": -1,
}
{
"content_hotel": -1,
"rate_hotel_id": 1
}
The validation has been done before the serializer create function and because you haven't create the contenthotel with that pk yet, the pk is invalid for that field(content_hotel). make content_hotel readonly in RateHotelSerializer and the problem will be fixed, change the serializer to this:
class RateHotelSerializer(serializers.ModelSerializer):
class Meta:
model = RateHotel
fields = __all__
read_only_fields = ('content_hotel', )
and also now you don't need to add content_hotel in objects of the list for rate_hotel, use a json like this:
{
"hotel_id": -1,
"rate_hotel": [{"rate_hotel_id": 1}]
}
If my models look like this:
class ImageUpload(models.Model):
created = models.DateTimeField(default=timezone.now)
iot_device = models.ForeignKey(IotDevice, related_name = 'uploads')
datafile = models.ImageField(upload_to = upload_path_handler)
class IotDevice(models.Model):
device_name = models.CharField(max_length=100, default='device1')
class Batch(models.Model):
begindate = models.DateTimeField(auto_now_add = False, blank = True, null = True)
enddate = models.DateTimeField(auto_now_add = False, blank = True, null = True)
iot_device = models.ForeignKey(IotDevice, related_name = 'batch_iotdevice')
I would like to select all ImageUpload where a batch is active (only a begindate, enddate needs to be NULL).
I wrote the following code in my view:
active_batch = ImageUpload.objects.filter(iot_device__batch_iotdevice__enddate__isnull = True, iot_device__batch_iotdevice__begindate__lte = F('created')).select_related('iot_device')
It generates the an almost correct SQL query when doing active_batch.query, but it doesn't include the api_batch fields.
SELECT
"api_imageupload"."id",
"api_imageupload"."created",
"api_imageupload"."iot_device_id",
"api_imageupload"."datafile",
"api_iotdevice"."id",
"api_iotdevice"."device_name"
FROM "api_imageupload"
INNER JOIN "api_iotdevice"
ON ("api_imageupload"."iot_device_id" = "api_iotdevice"."id")
INNER JOIN "api_batch"
ON ("api_iotdevice"."id" = "api_batch"."iot_device_id")
WHERE ("api_batch"."enddate" IS NULL AND "api_batch"."begindate" <= ("api_imageupload"."created"))
ORDER BY "api_imageupload"."created" DESC
Q1: How can I modify my filter query to return me the following serialized representation.
Q2: How can I write my serializers in a way that I would get Batch and every ImageUpload under it as child, e.x.:
[
{
"begindate": "2018-03-01T12:00:07.501686",
"enddate": "NULL"
"device_name": "CONTAINER_1",
"uploads": [
{
"created": "2018-06-18T12:00:07.501686",
"datafile": "/media/cam/CONTAINER_1/20180618_120007.jpg",
"thumbnail_path": "/media/cache/80/d3/80d34e2e28773d2c2e11548525e9fd19.jpg"
},
{
"created": "2018-06-18T00:00:05.017622",
"datafile": "/media/cam/CONTAINER_1/20180618_000005.jpg",
"thumbnail_path": "/media/cache/b7/f8/b7f836e52b975a61558ae62d0a6132a6.jpg"
},
]
},
{
"begindate": "2018-06-01T12:00:07.501686",
"enddate": "NULL"
"device_name": "CONTAINER_2",
"uploads": [
{
"created": "2018-06-18T12:00:07.501686",
"datafile": "/media/cam/CONTAINER_2/20180622_120007.jpg",
"thumbnail_path": "/media/cache/80/d3/80d34e2e287353532e11548525e9fd19.jpg"
},
]
}
]
Thanks
serializers.py
class ImageUploadSerializer(ModelSerializer):
thumbnail_path = serializers.SerializerMethodField()
def get_thumbnail_path(self, instance):
return ''
class Meta:
model = ImageUpload
fields = ('id', 'created', 'datafile', 'thumbnail_path')
class BatchSerializer(ModelSerializer):
iot_device = serializers.StringRelatedField()
uploads = serializers.SerializerMethodField()
def get_uploads(self, instance):
queryset = ImageUpload.object.filter(iot_device=instance.iot_device, created__gte=instance.begindate, created__lte=instance.enddate)
return ImageUploadSerializer(queryset, many=True).data
class Meta:
model = Batch
fields = ('id', 'begindate', 'iot_device', 'uploads')
views.py:
class BatchViewSet(ModelViewSet):
queryset = Batch.objects.all()
serializer_class = BatchSerializer
permission_classes = (IsAuthenticated,)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
serializer = BatchSerializer(queryset, context=self.get_serializer_context(), many=True)
return Response(serializer.data)
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.