Django REST Framework Foreign Key - cannot create object from JSON - django

I have following setup
Models
class Day(models.Model):
date = models.DateField(auto_now=False, auto_now_add=False)
price = models.FloatField()
paymentMethod = models.CharField(max_length = 200)
class Reservation(models.Model):
start = models.DateField(verbose_name='Заезд', auto_now=False, auto_now_add=False)
end = models.DateField(verbose_name='Выезд', auto_now=False, auto_now_add=False)
hasRefund = models.BooleanField(verbose_name='Возвратная бронь', default=True)
room = models.ForeignKey('Room', verbose_name='Номер', on_delete=models.CASCADE)
day = models.ManyToManyField(Day, blank=True)
check_in_time = models.CharField(verbose_name='Время заезда', max_length=200)
check_out_time = models.CharField(verbose_name='Время выезда', max_length=200)
guest_name = models.CharField(verbose_name='Имя гостя', max_length=200, blank=True)
payed = models.BooleanField(verbose_name='Оплачено', default=False)
class Room(models.Model):
name = models.CharField(max_length = 200, null=True)
Views
class ReservationCreateAPIView(CreateAPIView):
queryset = Reservation.objects.all()
serializer_class = ReservationSerializer
Serializers
class DaySerializer(serializers.ModelSerializer):
class Meta:
model = Day
fields = [
'date',
'price',
'paymentMethod',
]
class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = Room
fields = [
'pk',
'name',
]
class ReservationSerializer(serializers.ModelSerializer):
room = RoomSerializer
day = DaySerializer(many=True)
class Meta:
model = Reservation
fields = [
'start',
'end',
'hasRefund',
'room',
'day',
'check_in_time',
'check_out_time',
'guest_name',
'payed',
]
def create(self, validated_data):
day_data = validated_data.pop('day')
room_data = validated_data.pop('room')
reservation = Reservation.objects.create(**validated_data)
room = Room.objects.get_or_create(name=room_data['name'])
reservation.room.add(room)
for day in day_data:
day, created = Day.objects.get_or_create(date=day['date'], price=day['price'], paymentMethod=day['paymentMethod'])
reservation.day.add(day)
return reservatrion
Data, that I try to save
{
"start": "2017-12-12",
"end": "2017-12-12",
"hasRefund": false,
"room": 2,
"day": [ {
"date": "2017-12-12",
"price": "2",
"paymentMethod": "3"
},
{
"date": "2017-12-12",
"price": "2",
"paymentMethod": "3"
}
],
"check_in_time": "14-00",
"check_out_time": "12-00",
"guest_name": "Ivan",
"payed": false
}
Response that I get
IntegrityError at /core/create/
NOT NULL constraint failed: core_reservation.room_id
What Am I doing wrong ?

You try to create a Reservation without its Room. Your reservation model doesn't allow a null value for its room.
One way is to create a room first, then assign it to the reservation:
room_data = validated_data.pop('room')
room = Room.objects.get_or_create(name=room_data['name'])
validated_data.update({'room': room})
reservation = Reservation.objects.create(**validated_data)
You can also simply allow reservations without a room and add the room afterwards. In your model use:
room = models.ForeignKey(
'Room',
null=True, blank=True,
verbose_name='Номер',
on_delete=models.CASCADE,
)
Then add the room to a reservation instance:
room_data = validated_data.pop('room')
reservation = Reservation.objects.create(**validated_data)
room = Room.objects.get_or_create(name=room_data['name'])
reservation.room = room
reservation.save()

Related

Writable nested serializer method getting an Error while posting a request

models.py
class Client(models.Model):
client_id = models.AutoField(unique=True, primary_key=True)
org = models.ForeignKey(Organisation, on_delete=models.CASCADE, related_name='org',null=True)
product = models.ManyToManyField(Product,related_name='product')
client_name = models.CharField(max_length=100)
client_code = models.CharField(max_length=20)
client_logo = models.ImageField(upload_to=upload_to,storage=DownloadableS3Boto3Storage, null=True, blank=True)
currency = MoneyField(max_digits=10, decimal_places=2, default_currency='INR', null=True)
billing_method = models.CharField(max_length=40)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
email_id = models.EmailField(max_length=100)
contact_no = models.CharField(max_length=20)
mobile_no = models.CharField(max_length=20)
description = models.TextField(max_length=500)
street_address = models.CharField(max_length=250)
city = models.CharField(max_length=50)
state = models.CharField(max_length=50)
country = models.CharField(max_length=50)
pincode = models.CharField(max_length=10)
industry = models.CharField(max_length=100)
company_size = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.IntegerField(default=0, choices=STATUS_CHOICES)
class Meta:
db_table = "client_master"
def __str__(self):
return self.client_name
serializers.py
class Client_Serializers(serializers.ModelSerializer):
#product_name = Product_Serializers(many=True)
product = Product_Serializers(many=True)
class Meta:
model = Client
fields = ('client_id','currency','billing_method','first_name','last_name','description','street_address','city','state','country','pincode','industry','company_size','client_name', 'contact_no','mobile_no', 'email_id','client_logo','client_code','product',)
def create(self, validated_data):
products_data = validated_data.pop('product')
product = Product.objects.create(**validated_data)
for product_data in products_data:
Product.objects.create(product=product, **product_data)
return product
Data receiving on GET method
{
"client_id": 3,
"currency": "0.05",
"billing_method": "credit card",
"first_name": "career",
"last_name": "lab",
"description": "NA",
"street_address": "tiliconveli",
"city": "tirunelveli",
"state": "tamilnadu",
"country": "India",
"pincode": "600200",
"industry": "software",
"company_size": 100,
"client_name": "techfetch",
"contact_no": "1234567890",
"mobile_no": "1234567890",
"email_id": "icanio#gamil.com",
"client_logo": "https://icanio-project-management.s3.amazonaws.com/client_logo/sup_bat_fDauRxK.jpg",
"client_code": "TFH",
"product": [
{
"product_id": 5,
"product_name": "time"
}
]
}
But while posting it in the same format it is not getting posted, showing like
{
"status": "error",
"code": 400,
"data": {
"product": [
"This field is required."
]
},
"message": "success"
}
Views.py for reference
class Client_Viewset(DestroyWithPayloadMixin,viewsets.ModelViewSet):
renderer_classes = (CustomRenderer, )
queryset=models.Client.objects.all()
serializer_class=serializers.Client_Serializers
parser_classes = [MultiPartParser, FormParser]
filter_fields = (
'client_id',
'client_name',
'client_code',
'org_id',
)
How can I post the same data which I get in the GET request of Product field. Please help me resolve this as I was stuck in there for two days. I tried so many ways and end up not getting posted.
Product model
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
product serializer
class Product_Serializers(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('product_id','product_name',)
From your code, the Client_Serializers is for Client model but the create method is not creating any Client object.
Client_Serializers should be something on these lines -
class Client_Serializers(serializers.ModelSerializer):
product = Product_Serializers(many=True)
class Meta:
model = Client
fields = ('client_id','currency','billing_method','first_name','last_name','description','street_address','city','state','country','pincode','industry','company_size','client_name', 'contact_no','mobile_no', 'email_id','client_logo','client_code','product',)
def create(self, validated_data):
products_data = validated_data.pop('product')
client = Client.objects.create(**validated_data) # Create a client object
for product_data in products_data:
Product.objects.create(client=client, **product_data)
return client

Django - Too many similar queries

I'm creating a music rating app and I'm making a serializer for albums which has many relations and one aggregation method serializer which I think is causing all the trouble. The method gets average and count of reviews for every album. Is there any way I can decrease the number of queries for more performance?
All my models
class Artist(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255)
image = models.FileField(null=True, blank=True,
upload_to=rename_artist_image)
background_image = models.FileField(
null=True, blank=True, upload_to=rename_artist_bg_image)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self) -> str:
return self.name
class Album(models.Model):
RELEASE_TYPE_ALBUM_CHOICES = [
("LP", "LP"),
("EP", "EP"),
("Single", "Single"),
("Live", "Live"),
]
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255)
release_date = models.DateField(null=True)
artist_id = models.ForeignKey(
Artist, on_delete=models.PROTECT, related_name="albums"
)
art_cover = models.FileField(
null=True, blank=True, upload_to=rename_album_art_cover)
release_type = models.CharField(max_length=10,
choices=RELEASE_TYPE_ALBUM_CHOICES, default="LP")
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self) -> str:
return self.title
class Genre(models.Model):
name = models.CharField(max_length=255)
def __str__(self) -> str:
return self.name
class AlbumGenre(models.Model):
album_id = models.ForeignKey(
Album, on_delete=models.PROTECT, related_name="album_genres")
genre_id = models.ForeignKey(
Genre, on_delete=models.PROTECT, related_name="album_genres")
def __str__(self) -> str:
return self.genre_id.name
class AlbumLink(models.Model):
SERVICE_NAME_CHOICES = [
("spotify", "Spotify"),
("tidal", "Tidal"),
("amazonMusic", "Amazon Music"),
("appleMusic", "Apple Music"),
]
service_name = models.CharField(
max_length=15, choices=SERVICE_NAME_CHOICES)
url = models.CharField(max_length=255)
album_id = models.ForeignKey(
Album, on_delete=models.PROTECT, related_name="album_links")
def __str__(self) -> str:
return f"{self.service_name} - {self.url}"
class Track(models.Model):
title = models.CharField(max_length=255)
position = models.PositiveIntegerField()
album_id = models.ForeignKey(
Album, on_delete=models.PROTECT, related_name="tracks")
duration = models.DurationField(null=True)
def __str__(self) -> str:
return f"{self.position}. {self.title} - {self.duration}"
class AlbumOfTheYear(models.Model):
album_id = models.OneToOneField(
Album, on_delete=models.PROTECT, related_name="aoty")
position = models.IntegerField()
def __str__(self) -> str:
return str(self.position)
class Review(models.Model):
reviewer_id = models.ForeignKey(Reviewer, on_delete=models.PROTECT)
rating = models.IntegerField(
validators=[MaxValueValidator(100), MinValueValidator(0)]
)
review_text = models.TextField(null=True)
album_id = models.ForeignKey(
Album, on_delete=models.PROTECT, related_name="reviews")
created_at = models.DateTimeField(auto_now_add=True)
That's how album serializer looks.
"id": 2,
"title": "OK Computer",
"slug": "ok-computer",
"created_at": "2022-02-22T21:51:52.528148Z",
"artist": {
"id": 13,
"name": "Radiohead",
"slug": "radiohead",
"image": "http://127.0.0.1:8000/media/artist/images/radiohead.jpg",
"background_image": "http://127.0.0.1:8000/media/artist/bg_images/radiohead.jpg",
"created_at": "2022-02-22T00:00:00Z"
},
"art_cover": "http://127.0.0.1:8000/media/album/art_covers/ok-computer_cd5Vv6U.jpg",
"genres": [
"Alternative Rock",
"Art Rock"
],
"reviews": {
"overall_score": null,
"number_of_ratings": 0
},
"release_date": "1997-05-28",
"release_type": "LP",
"tracks": [
{
"position": 1,
"title": "Airbag",
"duration": "00:04:47"
},
{
"position": 2,
"title": "Paranoid Android",
"duration": "00:06:27"
}
],
"links": [
{
"service_name": "spotify",
"url": "https://open.spotify.com/album/6dVIqQ8qmQ5GBnJ9shOYGE?si=L_VNH3HeSMmGBqfiqKiGWA"
}
],
"aoty": null
Album serializer with method that gets average and count of reviews of album
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True)
genres = StringRelatedField(
source="album_genres", many=True, read_only=True)
aoty = StringRelatedField(read_only=True)
links = AlbumLinkSerializer(
source="album_links", many=True, read_only=True)
artist = SimpleArtistSerializer(source="artist_id")
def get_avg_and_count_of_reviews(self, album: Album):
reviews = Review.objects.only("rating").filter(album_id=album.id).aggregate(
overall_score=Avg(F("rating"), output_field=IntegerField()), number_of_ratings=Count(F("rating"), output_field=IntegerField()))
return reviews
reviews = serializers.SerializerMethodField(
method_name="get_avg_and_count_of_reviews")
class Meta:
model = Album
fields = ["id",
"title",
"slug",
"created_at",
"artist",
"art_cover",
"genres",
"reviews",
"release_date",
"release_type",
"tracks",
"links",
"aoty"]
# Save slug
def create(self, validated_data):
slug = slugify(validated_data["title"])
return Album.objects.create(slug=slug, **validated_data)
Here is a queryset in album Viewset
class AlbumViewSet(ModelViewSet):
queryset = Album.objects.prefetch_related("tracks").prefetch_related("album_genres").prefetch_related(
"album_links").prefetch_related("reviews").select_related("aoty").select_related("artist_id").all()
First you need to change your aggregate that you call once for every Album to an annotation, this will remove all of those extra aggregation queries
class AlbumViewSet(ModelViewSet):
queryset = Album.objects.prefetch_related(
"tracks",
"album_genres",
"album_links",
"reviews"
).select_related(
"aoty",
"artist_id"
).annotate(
overall_score=Avg(F("reviews__rating"), output_field=IntegerField()),
number_of_ratings=Count(F("reviews__rating"), output_field=IntegerField())
)
Now you can replace your reviews field with two regular IntegerFields
class AlbumSerializer(serializers.ModelSerializer):
...
overall_score = serializers.IntegerField(source="overall_score")
number_of_ratings = serializers.IntegerField(source="number_of_ratings")

Model with foreign keys taking ~90 seconds per query (foreign key model / serializer problem I think)

I'm having trouble with both my serializer and models for a table using foreign keys. I have a view for my Cost table ( see below ) that when I query, I get the following output in about 300-400 ms :
[
{
"id": 12,
"hours1": 10,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12",
"employee": 14,
"job1": 417,
"job2": 671,
"job3": 671,
"job4": 671
},
{
"id": 13,
"hours1": 8,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12",
"employee": 10,
"job1": 411,
"job2": 671,
"job3": 671,
"job4": 671
}
]
The employee, job1, job2, job3, job4 fields are foreign key IDs that I wish to see their main/primary value for (in this case, names!). I've played around with a serializer to achieve this, however, the problem is that it takes about 90 seconds per query and keeps getting longer!
[
{
"id": 12,
"employee": {
"employee_name": "Person 1"
},
"job1": {
"No": "30201"
},
"job2": {
"No": "N/A"
},
"job3": {
"No": "N/A"
},
"job4": {
"No": "N/A"
},
"hours1": 10,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12"
},
{
"id": 13,
"employee": {
"employee_name": "Person 2"
},
"job1": {
"No": "30101"
},
"job2": {
"No": "N/A"
},
"job3": {
"No": "N/A"
},
"job4": {
"No": "N/A"
},
"hours1": 8,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12"
}
]
My schema and serializers:
class Employee(models.Model):
employee_name = models.CharField(max_length=200, unique=True)
employee_email = models.CharField(max_length=200)
employee_navID = models.CharField(max_length=200)
employee_department = models.CharField(max_length=200)
joining_date = models.DateField()
leaving_date = models.DateField()
weekly_salary = models.IntegerField(default=0)
car_allowance = models.IntegerField()
national_insurance = models.IntegerField()
pension = models.IntegerField()
created_at = models.DateField(auto_now_add=True)
#property
def status(self):
if(self.leaving_date >= date.today()):
return "active"
else:
return "inactive"
#property
def employee_cost(self):
return self.weekly_salary + self.car_allowance + self.national_insurance + self.pension
class Meta:
verbose_name_plural = "Employees"
def __str__(self):
return self.employee_name
class ExcelJobsList(models.Model):
No = models.CharField(max_length=200)
Description = models.CharField(max_length=200)
Custom_No = models.CharField(max_length=200)
Sell_to_Name = models.CharField(max_length=200)
Status = models.CharField(max_length=200)
Person_Responsible = models.CharField(max_length=200)
Region_Code = models.CharField(max_length=200)
Market_Code = models.CharField(max_length=200)
Agreement_Form_Code = models.CharField(max_length=200)
Technology_Code = models.CharField(max_length=200)
secondappr_Code = models.CharField(max_length=200)
Search_Description = models.CharField(max_length=200)
Work_in_Progress_Status = models.CharField(max_length=200)
Job_Category = models.CharField(max_length=200)
Project_Manager = models.CharField(max_length=200)
Sales_Person = models.CharField(max_length=200)
Payment_Terms_Code = models.CharField(max_length=200)
First_Agreement_No = models.CharField(max_length=200)
No_of_Service_Agreements = models.CharField(max_length=200)
CRM_Reference = models.CharField(max_length=200)
class Meta:
verbose_name_plural = "Jobs"
def __str__(self):
return self.No
These are populated with a unique list of employees and jobs respectively. I then use to this to create a basic timesheet form where users can assign hours to a job (up to 4). This is posted to the below model, which has foreign keys to the employee and job table.
class Cost(models.Model):
employee = models.ForeignKey(
Employee, default=1, on_delete=SET_DEFAULT)
job1 = models.ForeignKey(ExcelJobsList, default=0, on_delete=SET_DEFAULT)
hours1 = models.IntegerField()
job2 = models.ForeignKey(
ExcelJobsList, default=0, on_delete=SET_DEFAULT, related_name="job2")
hours2 = models.IntegerField()
job3 = models.ForeignKey(
ExcelJobsList, default=0, on_delete=SET_DEFAULT, related_name="job3")
hours3 = models.IntegerField()
job4 = models.ForeignKey(
ExcelJobsList, default=0, on_delete=SET_DEFAULT, related_name="job4")
hours4 = models.IntegerField()
date = models.DateField()
This produces the follow output after I've selected the fields I wanted from a serializer:
class JobModelSerializer(serializers.ModelSerializer):
class Meta:
model = ExcelJobsList
fields = ['No']
class EmployeeModelSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
fields = ['employee_name']
class CostModelSerializer(serializers.ModelSerializer):
employee = EmployeeModelSerializer()
job1 = JobModelSerializer()
job2 = JobModelSerializer()
job3 = JobModelSerializer()
job4 = JobModelSerializer()
class Meta:
model = Cost
fields = ('__all__')
class CostListFilter(filters.FilterSet):
class Meta:
model = Cost
fields = {
'employee': ['exact'],
}
class JoinedFilterCostList(generics.ListAPIView):
queryset = Cost.objects.filter()
serializer_class = CostModelSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = CostListFilter
I've tried using to_field= 'xyz', however, this ends up returning null so either I'm missing something in my model or the serializer isn't set up correctly for it... or both!
Any help would be much really appreciated.
The slowness is caused by having to hit the database 4 times per Cost instance to get all the related ExcelJobsList.
To avoid this and make it more efficient, you can use select_related to the related ExcelJobsList like this:
class JoinedFilterCostList(generics.ListAPIView):
...
queryset = Cost.objects.select_related('job1', 'job2', 'job3', 'job4')
...
These should in turn end up with just one query, with inner joins on the related ExcelJobsList.

Can't represent all fields of a serialized ManyToMany through model with Django Rest Framework

I'm using Django and Django Rest Framework to represent a 'BaseRecipe' model. This model has a M2M field to represent the ingredients of that recipe. That M2M field relates a 'Product' object with the 'BaseRecipe' object and extends that two models with another field to represent the 'quantity'. I'm trying to retrieve a list of those ingredients in the BaseRecipeSerializer, but only the id's are returned.
Any ideas why?
Thank you in advance!
My models.py:
class Product(models.Model):
name = models.CharField(_('Name'), max_length=255, help_text=_('Product name'))
supplier = models.ForeignKey(Supplier, blank=True, related_name='supplier_products', on_delete=models.CASCADE)
serial_no = models.CharField(_('Serial number'), max_length=50, blank=True,
help_text=_('Product serial number. Max 50 characters.'))
allergens = models.ManyToManyField(Allergen, blank=True, related_name='product_allergens')
description = models.TextField(_('Description'), blank=True, help_text=_('Additional product information.'))
is_vegan = models.BooleanField(_('Vegan'), default=False)
is_halal = models.BooleanField(_('Halal'), default=False)
format_container = models.CharField(_('Format container'), max_length=6, choices=CONTAINERS, blank=True,
help_text=_('Package format'))
format_quantity = models.DecimalField(_('Format quantity'), blank=True, null=True, max_digits=9, decimal_places=3,
help_text=_('Format quantity sell'))
format_unit = models.CharField(_('Format unit'), max_length=6, choices=UNITS, blank=True)
quantity = models.DecimalField(_('Quantity'), blank=True, null=True, max_digits=9, decimal_places=3,
help_text=_('Quantity per unit provided'))
type = models.CharField(_('Type'), max_length=255, help_text=_('Type'), blank=True)
unit = models.CharField(_('Unit'), max_length=6, choices=UNITS)
unit_price = models.DecimalField(_('Unit price'), blank=True, null=True, max_digits=9, decimal_places=3)
class Meta:
ordering = ['name', ]
#property
def price(self) -> Decimal:
return self.quantity * self.unit_price
def __str__(self):
return self.name
class BaseRecipe(models.Model):
user = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='user_base_recipes')
restaurant = models.ForeignKey(Restaurant, null=True, on_delete=models.SET_NULL,
related_name='restaurant_base_recipes')
title = models.CharField(_('Base recipe title'), max_length=255, help_text=_('Base recipe. Example: Chicken broth'),
blank=True)
elaboration = models.TextField(_('Elaboration'), blank=True, help_text=_('Base recipe making instructions.'))
quantity = models.DecimalField(_('Quantity'), max_digits=9, decimal_places=3,
help_text=_('Quantity produced with this recipe (after cooking). '))
unit = models.CharField(_('Unit'), max_length=6, choices=UNITS)
expiry_date = models.DateField(_('Expiry date'), help_text=_('Last day product is safe to consume.'))
ingredients = models.ManyToManyField(Product, through='IngredientBaseRecipe', related_name='base_recipe_ingredients')
image = models.ImageField(_('Picture'), upload_to=get_picture_path, blank=True, null=True)
exhausted = models.BooleanField(_('Exhausted'), default=False, help_text=_('If it\'s needed to produce more.'))
next_batch_date = models.DateField(_('Next batch date'), blank=True,
help_text=_('When it\'s necessary to prepare more of this base recipe.'))
storage = models.CharField(_('Storage'), max_length=12, choices=STORAGE_METHODS, blank=True)
class Meta:
ordering = ['-id']
#property
def cost(self) -> Decimal:
return sum([ingredient.cost for ingredient in self.ingredients.all()])
#property
def allergens(self):
allergens_ids = IngredientBaseRecipe.objects.filter(base_recipe=self).values_list('product__allergens',
flat=True)
allergens = Allergen.objects.filter(id__in=allergens_ids)
return allergens
def __str__(self):
return self.title
class IngredientBaseRecipe(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
base_recipe = models.ForeignKey(BaseRecipe, on_delete=models.CASCADE)
product_quantity = models.DecimalField(_('Product quantity'), max_digits=9, decimal_places=3, default=0.0)
class Meta:
ordering = ['-id']
#property
def allergens(self):
return self.product.allergens.all()
#property
def cost(self) -> Decimal:
return self.quantity * self.product.unit_price
def __str__(self):
return self.product.name
serializer.py:
class SimpleProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['id', 'name', ]
class IngredientBaseRecipeSerializer(serializers.ModelSerializer):
# product_name = serializers.ReadOnlyField(source='product.name')
product = SimpleProductSerializer(read_only=True)
class Meta:
model = IngredientBaseRecipe
exclude = ['base_recipe', ]
class BaseRecipeSerializer(serializers.ModelSerializer):
ingredients = IngredientBaseRecipeSerializer(many=True, read_only=True)
allergens = AllergenSerializer(many=True, read_only=True)
cost = serializers.FloatField(read_only=True)
class Meta:
model = BaseRecipe
fields = '__all__'
Result:
{
"count": 3,
"next": null,
"previous": null,
"results": [
{
"id": 5,
"ingredients": [
{
"id": 1
},
{
"id": 3
},
{
"id": 3
}
],
"allergens": [
{
"id": 9,
"name": "Celery",
"icon": null
},
{
"id": 12,
"name": "Sulphites",
"icon": null
}
],
"title": "Tipical Spanish",
"elaboration": "No se tio.",
"quantity": "23232.000",
"unit": "l",
"expiry_date": "2021-02-28",
"image": null,
"exhausted": false,
"next_batch_date": "2021-03-01",
"storage": "freezer",
"user": 1,
"restaurant": 1
},
{
"id": 3,
"ingredients": [],
"allergens": [],
"cost": 0.0,
"title": "Chicken broth",
"elaboration": "One, two, three.",
"quantity": "45.000",
"unit": "l",
"expiry_date": "2021-02-28",
"image": null,
"exhausted": false,
"next_batch_date": "2021-03-01",
"storage": "freezer",
"user": 1,
"restaurant": 1
}
]
}
BaseRecipe.ingredients will return a queryset of Product instances but you are passing them to the IngredientBaseRecipeSerializer. You need to change the source for this field so that you pass the related IngredientBaseRecipe instances to this field
class BaseRecipeSerializer(serializers.ModelSerializer):
ingredients = IngredientBaseRecipeSerializer(
many=True,
read_only=True,
source='ingredientbaserecipe_set'
)
...

Django Rest Framework - can save foreign key with number value

I try to serialize and save following json
{
"start": "2017-12-12",
"end": "2017-12-12",
"has_refund": false,
"room": {
"key": 0
},
"reserved_days": [ {
"date": "2017-12-12",
"price": "2",
"paymentMethod": "3"
},
{
"date": "2017-12-13",
"price": "2",
"paymentMethod": "3"
}
],
"check_in_time": "14:00",
"check_out_time": "12:00",
"guest_name": "Ivan",
"payed": false
}
SERIALIZERS
class DaySerializer(serializers.ModelSerializer):
class Meta:
model = Day
fields = [
'date',
'price',
'paymentMethod',
]
class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = Room
fields = [
'key',
]
class ReservationSerializer(serializers.ModelSerializer):
room = RoomSerializer()
reserved_days = DaySerializer(many=True)
class Meta:
model = Reservation
fields = [
'start',
'end',
'check_in_time',
'check_out_time',
'reserved_days',
'room',
'has_refund',
'payed',
'guest_name',
'reservation_number',
]
def create(self, validated_data):
day_data = validated_data.pop('reserved_days')
room = validated_data.pop('room')
reservation = Reservation.objects.create(**validated_data)
existing_room = Room.objects.get(key=room['key'])
reservation.room = existing_room
reservation.save()
for day in day_data:
day, created = Day.objects.get_or_create(date=day['date'], price=day['price'], paymentMethod=day['paymentMethod'])
reservation.reserved_days.add(day)
return reservation
MODEL
class Day(models.Model):
date = models.DateField(auto_now=False, auto_now_add=False)
price = models.FloatField()
paymentMethod = models.CharField(max_length = 200)
def __unicode__(self):
return str(self.date)
class Room(models.Model):
name = models.CharField(max_length = 200, null=True)
key = models.AutoField(primary_key=True)
def __unicode__(self):
return self.name
class Reservation(models.Model):
start = models.DateField(verbose_name='Заезд', auto_now=False, auto_now_add=False, blank=False)
end = models.DateField(verbose_name='Выезд', auto_now=False, auto_now_add=False, blank=False)
check_in_time = models.TimeField(verbose_name='Время заезда', blank=False)
check_out_time = models.TimeField(verbose_name='Время выезда', blank=False)
has_refund = models.BooleanField(verbose_name='Возвратная бронь', default=True)
payed = models.BooleanField(verbose_name='Оплачено', default=False)
room = models.ForeignKey(Room, null=True, blank=True, verbose_name='Номер', on_delete=models.CASCADE)
reserved_days = models.ManyToManyField(Day, blank=False)
guest_name = models.CharField(verbose_name='Имя гостя', max_length=200, blank=True)
reservation_number = models.CharField(verbose_name='Номер брони', max_length=200, blank=True)
In response I get 500 error and this
KeyError at /core/create/
'key'
However, if I change field of room to name (inside room serializer and create method of reservation serializer), everything works perfectly
class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = Room
fields = [
'name',
]
class ReservationSerializer(serializers.ModelSerializer):
room = RoomSerializer()
reserved_days = DaySerializer(many=True)
class Meta:
model = Reservation
fields = [
'start',
'end',
'check_in_time',
'check_out_time',
'reserved_days',
'room',
'has_refund',
'payed',
'guest_name',
'reservation_number',
]
def create(self, validated_data):
day_data = validated_data.pop('reserved_days')
room = validated_data.pop('room')
reservation = Reservation.objects.create(**validated_data)
existing_room = Room.objects.get(name=room['name'])
reservation.room = existing_room
reservation.save()
for day in day_data:
day, created = Day.objects.get_or_create(date=day['date'], price=day['price'], paymentMethod=day['paymentMethod'])
reservation.reserved_days.add(day)
return reservation
Seems that it works only with strings.
How to make it work with number ? The same thing happened with me when I tried saving with pk instead of separated int fieled