Let\'s say I have a Basket
model and I want to validate that no more than 5
Item
s can be added to it:
class Basket(mod
You can never validate relationships in the clean method of the model. This is because at clean time, the model may not yet exist, as is the case with your Basket. Something that does not exist, can also not have relationships.
You either need to do your validation on the form data as pointed out by @bhattravii, or call form.save(commit=False)
and implement a method called save_m2m, which implements the limit.
To enforce the limit at the model level, you need to listen to the m2m_changed signal. Note that providing feedback to the end user is a lot harder, but it does prevent overfilling the basket through different means.
I've been discussing this on the Django Developers list and have in fact tabled a method of doing this for consideration in the Django core in one form or another. The method is not fully tested nor finalised but results for now are very encouraging and I'm employing it on a site of mine with success.
In principle it relies on:
Using PostgreSQL as your database engine (we're fairly sure it won't work on Lightdb or MySQL, but keen for anyone to test this) enter code here
Overriding the post() method of your (Class based) view such that it:
This is working wonderfully for me:
def post(self, request, *args, **kwargs):
# The self.object atttribute MUST exist and be None in a CreateView.
self.object = None
self.form = self.get_form()
self.success_url = reverse_lazy('view', kwargs=self.kwargs)
if connection.vendor == 'postgresql':
if self.form.is_valid():
try:
with transaction.atomic():
self.object = self.form.save()
save_related_forms(self) # A separate routine that collects all the formsets in the request and saves them
if (hasattr(self.object, 'full_clean') and callable(self.object.full_clean)):
self.object.full_clean()
except (IntegrityError, ValidationError) as e:
if hasattr(e, 'error_dict') and isinstance(e.error_dict, dict):
for field, errors in e.error_dict.items():
for error in errors:
self.form.add_error(field, error)
return self.form_invalid(self.form)
return self.form_valid(self.form)
else:
return self.form_invalid(self.form)
else:
# The standard Djangop post() method
if self.form.is_valid():
self.object = self.form.save()
save_related_forms(self)
return self.form_valid(self.form)
else:
return self.form_invalid(self.form)
And the conversation on the Developers list is here:
https://groups.google.com/forum/#!topic/django-developers/pQ-8LmFhXFg
if you'd like to contribute any experience you gain from experimenting with this (perhaps with other database backends).
The one big caveat in the above approach is it delegates saving to the post() method which in the default view is done in the form_valid() method, so you need to override form_valid() as well, otherwise a post() like the one above will see you saving the form twice. Which is just a waste of time on an UpdateView but rather disastrous on a CreateView.