问题
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
'sModelForm
has a clean method that does mainModel
instance's validation ModelAdmin
has access to theInlineFormset
s (https://github.com/django/django/blob/master/django/contrib/admin/options.py#L530)- The
ModelForm
does not have access to theModelAdmin
or theModelAdmin
's formsets (so it's clean method cannot cross validate the inlines) - The
BaseModelFormSet
(BaseInlineFormSet
extendsBaseModelFormSet
) for your inlines has aclean
method (see https://github.com/django/django/blob/master/django/forms/models.py#L635) but does not have access in any way to the mainModel
instance
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