Julien Mertz
Julien Mertz

Reputation: 485

How to add a value to a form field before rendering the form?

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

Answers (1)

dirkgroten
dirkgroten

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

Related Questions