Django InlineFormSet validation

江枫思渺然 提交于 2021-02-08 03:58:18

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!