I have a Django form (TestForm) that contains a single field, quantity. I also have a Django formset (TestFormset) that contains multiple instances of my TestForm.
I want to write a custom clean() method for my TestFormset that validates that the sum of the quantities specified within my multiple TestForms is equal to a number, max_quantity, stored in a session variable.
I know that I am able to perform this validation within views.py (for example, after my formset is validated and cleaned, I could manually sum up the 'quantity' variables in my TestForms and check to ensure that they are equal to request.session['max_quantity'], throwing an error if any problems are found).
But ideally I'd love to move all my form validation logic into the clean() method of forms.py. However, I can't figure out how to pass an external value into my Formset that is not linked to one of its individual forms.
Is this possible to do?
forms.py
from django.forms import BaseFormSet
class TestForm(forms.Form):
quantity = forms.IntegerField()
class BaseTestFormset(BaseFormset):
def clean(self):
if any(self.errors):
# Don't bother validating the formset unless each form is valid on its own
return
quantity = 0
for form in self.forms:
quantity += form.cleaned_data['quantity']
# IF QUANTITY IS NOT EQUAL TO MAX_QUANTITY, THROW AN ERROR...
# ...BUT HOW DO WE GET THE MAX_QUANTITY INTO THIS FUNCTION?
views.py
from .forms import TestForm, BaseTestFormset
def serve_form(request):
TestFormSet = formset_factory(TestForm, formset=BaseTestFormset)
if request.method == 'POST':
formset = TestFormSet(request.POST)
# This method should check to ensure that the sum of quantities within our formsets does not exceed max_quantity
if formset.is_valid():
# Proceed to take action
else:
# Sample initial data
formset = TestFormSet(initial=[{'quantity': 5}, {'quantity': 7}])
# I CAN PASS MAX_QUANTITY INTO THE TEMPLATE... BUT HOW DO I GET IT INTO THE FORMSET VALIDATION METHOD?
return render(request, 'template.html', {'formset': formset, 'max_quantity': request.session['max_quantity']}
As with forms, if you want access to something in a method you need to pass it in somewhere. You can do that in the initialiser if you like:
class BaseTestFormset(forms.BaseFormSet):
def __init__(self, *args, **kwargs):
self.max_quantity = kwargs.pop('max_quantity', None)
super(BaseTestFormset, self).__init__(*args, **kwargs)
def clean(self):
...
if quantity > self.max_quantity:
...
and in the view:
if request.method == 'POST':
formset = TestFormSet(request.POST, max_quantity=request.session['max_quantity'])