问题
I have a BaseInlineFormSet, and I'd like to validate a field in the parent form based on the values on the fields of the children. As seen in the docs, the only method to make a custom validation is clean(), but I can't find a way to add errors to the parent form, only for the children.
In the following code I build a formula. Each variable comes from a inner form, and if the global formula don't validate, I'd like to add an error to the parent's form field formula
class CustomInlineFormSet(BaseInlineFormSet):
def clean(self):
formula = self.instance.formula
variables = []
for form in self.forms:
if 'formula_variable' in form.cleaned_data:
variables.append(form.cleaned_data['formula_variable'])
valid, msg = validate_formula(formula, variables)
if not valid:
WHAT HERE???
回答1:
Look at these links:
Validation of dependant inlines in django admin
and
django - How to cross check ModelAdmin and its inlines?
But it seems like the gist is that:
- the
ModelAdmin'sModelFormhas a clean method that does mainModelinstance's validation ModelAdminhas access to theInlineFormsets (https://github.com/django/django/blob/master/django/contrib/admin/options.py#L530)- The
ModelFormdoes not have access to theModelAdminor theModelAdmin's formsets (so it's clean method cannot cross validate the inlines) - The
BaseModelFormSet(BaseInlineFormSetextendsBaseModelFormSet) for your inlines has acleanmethod (see https://github.com/django/django/blob/master/django/forms/models.py#L635) but does not have access in any way to the mainModelinstance
so what you are going to need to do is some hacking around using the top two links as a guide.
回答2:
Thanks to Russell Keith-Magee, I managed to solve this. I detail the answer here for future reference.
I was doing it wrong, the InlineFormSet was not the right place to do the kind of validation I needed, it should be done in the parent ModelForm. But the parent ModelForm don't have access to the child FormSet, so I just need to pass the FormSet to the ModelForm as a parameter. So the thing looks like this now:
The view:
class FormulaCreate(CreateView):
model = Formula
template_name = "whatever.html"
def get_context_data(self, **kwargs):
context = super(ParameterRatioCreate, self).get_context_data(**kwargs)
if self.request.POST:
formset = FormulaVariablesFormSet(self.request.POST, instance=self.object)
context['form'] = FormulaForm(formset=formset, data=self.request.POST, instance=self.object)
context['formset'] = formset
else:
formset = FormulaVariablesFormSet(instance=self.object)
context['form'] = FormulaForm(formset=formset, instance=self.object)
context['formset'] = formset
return context
def form_valid(self, form):
context = self.get_context_data()
form = context['form']
formset = context['formset']
if form.is_valid() and formset.is_valid():
ratio = form.save()
formset = FormulaVariablesFormSet(self.request.POST, instance=ratio)
if formset.is_valid():
formset.save()
self.object = ratio
return redirect(self.get_success_url())
context['formset'] = formset
return self.render_to_response(context)
The Form:
class FormulaForm(forms.ModelForm):
class Meta:
model = Formula
def __init__(self, formset, *args, **kwargs):
self.formset = formset
super(FormulaForm, self).__init__(*args, **kwargs)
def clean(self):
variables = {}
for form in self.formset:
if form.is_valid() and 'formula_variable' in form.cleaned_data:
variables[form.cleaned_data['formula_variable']] = 1
if 'formula' in self.cleaned_data:
formula = self.cleaned_data['formula']
valid, msg = do_formula(formula, variables)
if not valid:
self._errors['formula'] = self.error_class([msg])
del self.cleaned_data['formula']
return self.cleaned_data
The FormSet:
FormulaVariablesFormSet = inlineformset_factory(Formula,
FormulaVariable,
extra=1)
来源:https://stackoverflow.com/questions/23893167/django-inlineformset-validation