25đź‘Ť
I ran into this question while researching another problem. While digging through the Django source in search of a solution for my problem, I found the answer to this question so I’ll document it here:
When a form is allowed to have empty values (this applies for empty forms contained within a formset) and the submitted values haven’t been changed from the initial ones, the validation is skipped. Check the full_clean() method in django/forms/forms.py (line 265 in Django 1.2):
# If the form is permitted to be empty, and none of the form data has
# changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
I’m not sure what kind of solution you’re looking for (also, this question is already somewhat dated) but maybe this will help someone in the future.
16đź‘Ť
@Jonas, thanks. I used your description to solve my problem. I needed the a form to NOT validate when empty. (Forms added with javascript)
class FacilityForm(forms.ModelForm):
class Meta:
model = Facility
def __init__(self, *arg, **kwarg):
super(FacilityForm, self).__init__(*arg, **kwarg)
self.empty_permitted = False
facility_formset = modelformset_factory(Facility, form=FacilityForm)(request.POST)
It will make sure any displayed forms must not be empty when submitted.
2đź‘Ť
The forms created based on the “extra” parameter of “formset_factory” have their “empty_permitted” property set to True. (see: formset.py line 123)
# Allow extra forms to be empty.
if i >= self.initial_form_count():
defaults['empty_permitted'] = True
So it seems the better way to use “initial” parameter of the FormSet and not the “extra” parameter of “formset_factory” for this use case.
Please find the description at using-initial-data-with-a-formset
- Convert data on AlterField django migration
- Invalid parameter server_name in /etc/nginx/sites-enabled/django
- Django rest auth email instead of username
0đź‘Ť
Simple is better. Due a Formset is a list of forms, you could iterate this set.
if FormSet(request.POST,).is_valid():
for form in FormSet:
# Check if value is empty using value().
if form['field'].value():
# this form's field is not empty. Create and save object.
object = form.save()
- How to automatically add target="_blank" to external links only?
- Django update on queryset to change ID of ForeignKey
0đź‘Ť
I’m not sure what version of Django the OP was using, but this answer works for Django 4.1.
@Jonas is right; very little validation is performed on an empty formset by default. You could solve this by passing form_kwargs = {'empty_permitted': False}
, but that can cause other problems (for example, this prevents the template engine from being able to create a blank form for use with javascript using the {{ formset.empty_form }}
tag.
Depending on your use case, I think a better solution is to add a minimum form number validation. So OP’s view would include:
#GET
for prod in products:
ProductFormSet = modelformset_factory(
Product,
exclude=('date',),
extra=prod.amount,
min_num = 1, # This means that there must be at least 1 form.
validate_min = True, # This tells the formset validation to check min_num.
)
formsset.append(ProductFormSet(prefix="prod_%d"%prod.pk))
#POST
for prod in products:
ProductFormSet = modelformset_factory(
Product,
exclude=('date',),
extra=prod.amount,
min_num = 1, # This means that there must be at least 1 form.
validate_min = True, # This tells the formset validation to check
)
formsset.append(ProductFormSet(request.POST,prefix="prod_%d"%prod.pk))
If there isn’t at least one form that has changed, the form validation will fail as desired.
- Python / Django multi-tenancy solution
- Django : Convert UTC to local time zone in 'Views'
- How to unit test Django-CMS extensions?
- Django Admin: has_delete_permission Ignored for "Delete" Action