I have these forms and formset:
class MainForm(ModelForm):
class Meta:
model = Main
fields = [
'name',
'type',
]
def clean(self):
cleaned_data = super().clean()
type = cleaned_data.get('type')
errors = list()
if type == 'used':
records = # TODO: get RecordFormset data?
if len(records) == 0:
msg = 'At least one record should exist'
errors.append(ValidationError(msg))
if errors:
raise ValidationError(errors)
class RecordForm(ModelForm):
class Meta:
model = Record
fields = [
'count',
'price',
]
RecordFormset = inlineformset_factory(
Main, Record, form = RecordForm,
extra=1, can_delete = True, can_delete_extra = True,
)
Is it possible to get formset data in clean method of main form?
Not “properly”. (My gut reaction to this question is to wonder why you think it’s beneficial to do that.)
The form is a different object than the formset. The formset is not part of the form.
Now, having said that, you can pass your formset to the form constructor as an additional parameter, and work with it just like you would work with any other data, but this is blurring some boundary lines that don’t usually need to be crossed.
So would it be better to do this validation in view?
I don’t know what validation you’re talking about here.
What are you trying to determine?
If it’s to verify the number of submitted forms in the formset, then that’s a test for the formset. See validate_min and validate_max
I am trying to test whether there are some forms in formset, but only if the ‘type’ (from MainForm) has value ‘used’. If the ‘type’ has another value the number of forms in formset can be zero.
My approach for this would be to use the existing features for this.
I’d validate the form first, then check the type
value to set a local min_num
variable. I’d then use that value as the min_num
parameter in the formset_factory call.
Where to check the type
value? In the view?
That’s where I would do it. You’re creating the formset in the view, so that’s where you are going to be using the result of the test.
How can i set the min_num value from view?
This is my view:
formset_min_num = 1
...
def get_formset(self, formset_min_num):
return MyFormset(self.request.POST or None, instance=self.object, min_num = formset_min_num)
This is my formset:
MyFormset = inlineformset_factory(
ParentModel, SubModel, form = MyForm,
extra=0, validate_min=True,
can_delete = True, can_delete_extra = True,
)
This is the error message I got:
BaseFormSet.__init__() got an unexpected keyword argument 'min_num'
The max_num, min_num parameters are parameters to the inlineformset_factory function.
You can either set that as a parameter in your call to inlineformset_factory, or you can directly set the attribute on the created formset after creating it but before creating the formset instance:
MyFormset.min_num = 1
or you can set it on the formset instance itself:
my_formset = MyFormset(...)
my_formset.min_num = 1
Nevertheless… The formset is validated for min_num and form cannot be saved without desired num of forms in formsets. That’s ok. But I cannot see the validation errors. Should they be saved in formset.non_form_errors?
Yes, that’s what the docs indicate.
Is it possible to change too_few_errors?
This code is not working:
MyFormset = inlineformset_factory(
ParentModel, SubModel, form = MyForm,
extra=0,
validate_min=True,
can_delete = True, can_delete_extra = True,
error_messages={'too_few_forms': 'TEST',},
)
I still see the default message:
Please submit at least 1 form.
According to the docs, yes.
There’s not enough information here for me to diagnose this.
I’d have to see the complete view and all directly related functions in order to verify that you’re doing this all correctly.
So I changed too_few_erros
in the view:
formset.error_messages['too_few_forms'] = 'test'
Now it works. I wonder why it wasn’t possible to change it in the forms.py