Reputation: 485
I'm building a website whose purpose is to deliver food. I have a model "Plat" representing a meal and a model "Commande" representing an order. In the website, the user can look at a meal, then click on a button "make an order" and finally fill a form with the order details. "Commande" has multiple fields and one of them is "plat" that link the order to the meal. My problem is that I need to fill the "plat" field of the "Commande" model before rendering the form because I have a validation rule that depends on a field of the "Plat" model : in the "Commande" form, the customer can choose the number of parts of the meal he want, and if this number is greater than the number of parts availables (parts_available is a filed of "Plat"), it raises an error.
I have tried to do it this way (see below) but that doesn't work, when the customer save the "Commande" form, it raises an AttributeError because it try to access a field of plat but plat is equal to None. Do you have any idea how I can do what I want ?
Thanks in advance !
models.py
class Plat(models.Model):
SOME FIELDS
nb_portions = models.IntegerField(verbose_name = "Nombre de portions disponibles") # the number of parts available
class Commande(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
plat = models.ForeignKey('Plat', on_delete = models.CASCADE) # models.PROTECT pour pas supprimer, models.CASCADE pour supprimer.
nb_portions = models.IntegerField(verbose_name = "Nombre de portions")
forms.py
class CommandeForm(forms.ModelForm):
class Meta:
model = Commande
fields = ['nb_portions', 'adresse_livraison']
widgets = {'nb_portions': forms.NumberInput(attrs={'class': 'form-control'}),
'adresse_livraison': forms.TextInput(attrs={'class': 'form-control'})}
labels = {'adresse_livraison': 'Adresse de livraison (livraison à partir de 5 portions) :'}
def clean(self):
cleaned_data = super(CommandeForm, self).clean()
plat = cleaned_data.get('plat')
nb_portions = cleaned_data.get('nb_portions')
if nb_portions > plat.nb_portions:
self.add_error("Le nombre de portions maximales disponibles pour ce plat est : " + str(plat.nb_portions) + " portions.")
return cleaned_data
views.py
def commander(request, id):
plat = get_object_or_404(Plat, id=id)
if request.method == "POST":
form = CommandeForm(request.POST)
if form.is_valid():
commande = form.save(commit = False)
commande.user = request.user
commande.plat = plat
commande.save()
return redirect(commande_validee)
else:
commande = CommandeForm()
commande.plat = plat
form = commande
return render(request, 'actualites/commander.html', locals())
EDIT :
I have copy-pasted the init method suggested by dirkgroten in the CommandeForm and modified the view accordingly :
def commander(request, id):
plat = get_object_or_404(Plat, id=id)
if request.method == "POST":
form = CommandeForm(request.POST, plat=plat)
if form.is_valid():
commande = form.save(commit = False)
commande.user = request.user
commande.date = plat.date_prep
commande.save()
return redirect(commande_validee)
else:
form = CommandeForm(plat=plat)
return render(request, 'actualites/commander.html', locals())
Upvotes: 0
Views: 155
Reputation: 20672
Your form's cleaned_data
doesn't have an attribute plat
because it isn't a field in your form. You should pass plat
as extra parameter to your form's init
method so you can use it later:
form = CommandeForm(request.POST, plat=plat)
class CommandeForm(...):
def __init__(self, *args, **kwargs):
self.plat = kwargs.pop('plat', None)
super().__init__(*args, **kwargs)
def clean(self):
...
if self.plat and nb_portions > self.plat.nb_portions:
...
Upvotes: 1