I’m a beginner, I discover many things and sometimes I feel overwhelmed about all the informations and tricks to learn.
I try to do a django project for a pizzeria. People can order pizza online, the order is simple : you can choose one or more pizza and optionally add some Extras (adding cheese, ham or whatever…) for each pizza you ordered and it’s added to the cart.
My problem is to calculate automatically the price for each pizza.
Basically here’s my Model file:
class Pizza(models.Model):
nom = models.CharField(max_length=30)
ingrédients = models.CharField(max_length=500)
prix = models.DecimalField(max_digits=4, decimal_places=2)
nom means name
class Extra(models.Model):
sup = models.CharField(max_length=30)
prix = models.DecimalField(max_digits=3, decimal_places=2)
sup is the name of the extra.
class IndividualChoice(models.Model):
pizzaChosen = models.ForeignKey(‘Pizza’, default="", null=True, on_delete=models.CASCADE)
extraChosen = models.ManyToManyField(‘Extra’, blank=True)
panier = models.ForeignKey(‘Cart’, default="", on_delete=models.CASCADE)
calzone = models.BooleanField(default=False)
prix = models.DecimalField(max_digits=5, decimal_places=2, default=0)
IndividualChoice is a bit weird. It’s a model which stores each choice, here “panier” means Cart which has a model but I don’t think it’s useful to make it appear here.
I learned a bit about django signals, so I tried to create one:
def prix_extra_calcul(sender, instance, action, *args, **kwargs):
instance.prix = 0
if action == “post_add” or action == “post_remove” or action == “post_clear”:
for extra in instance.extraChosen.all():
instance.prix += extra.prix
instance.prix += instance.pizzaChosen.prix
instance.save()
m2m_changed.connect(prix_extra_calcul, sender=IndividualChoice.extraChosen.through)
It works well when I create a pizza with extra but if only change the pizza the m2m signal won’t be fired. So I tried to find another solution but it’s stupid:
def prix_pizza_calcul(sender, instance, *args, **kwargs):
instance.prix = 0
for extra in instance.extraChosen.all():
instance.prix += extra.prix
instance.prix += instance.pizzaChosen.prix
def prix_extra_calcul(sender, instance, action, *args, **kwargs):
if action == “post_add” or action == “post_remove” or action == “post_clear”:
# no need to write code because prix_pizza_calcul will be fired by instance.save()
instance.save()
pre_save.connect(prix_pizza_calcul, sender=IndividualChoice)
m2m_changed.connect(prix_extra_calcul, sender=IndividualChoice.extraChosen.through)
The second solution "works well" when I want to modify a choice but when I create a new pizza I have this error message “maximum recursion depth exceeded while calling a Python object”. And I think it’s because i’m looping over something not saved.
I’m completely stuck with this problem, I tried to solve it for several days. Should I create a Signal, is a signal is appropriate for this kind of problem ?
(What a loooong way to be a web developer…)
Thanks for reading !
I don't know your knowledge about database normalization, but here with the price on IndividualChoice, you're duplicating data: the price is just a computation of already stored data. If you avoid duplicating data, you'll avoid data anomalies.
If you have a first name and a last name, would you store the "full name" ? If your answer is yes, please read the documentation about @property here: https://docs.djangoproject.com/en/3.0/topics/db/models/#model-methods
A solution is probably removing the DecimalField prix
on the IndividualChoice
model and add something like this:
@property
def prix(self):
"Returns the price of the item"
price = self.pizzaChosen.prix
for extra in self.extraChosen.all():
price += extra.prix
return price