Django自定义formset验证以及来自其他表单的其他数据

时间:2016-04-18 13:56:03

标签: python django django-forms formset django-validation

要验证我的formset,我需要使用其他数据(在formset之外)。当它位于BaseFormSet时,如何将该数据传递给验证功能。除了我的formset,request.POST还包含我需要验证的country_id。此外,我还需要验证期间的会话用户。这是我的验证码:

class BaseShippingTupleFormSet(BaseFormSet):

    def clean(self):
        if any(self.errors):
            return

        for form in self.forms:
            country = TranslatedCountry.objects.get(id=country_id) #  how to get country
            shippings = ShippingTuple.objects.filter(country=country, user=user) #  how to get user
            for s in shippings:
                if value_from > s.value_from and value_from < s.value_to:
                    raise forms.ValidationError(("Please enter a value that does not overlap with already existing "
                                                 "shipping tuples for the same country ("+country.translated_name+")"))

内联注释指示缺少变量的位置(country_iduser)。任何提示或方向都表示赞赏。

解决方案

在添加@Evgeny Barbashov回答建议的自定义__init__方法并解决其他一些问题之后,这是可行的解决方案:

def view_with_args(request, country_id, amount):
    context = {}
    user = request.user
    country = TranslatedCountry.objects.get(id=country_id)

    CustomArgsFormSet = formset_factory(ShippingTupleForm, formset=BaseShippingTupleFormSet, extra=int(amount)-1)
    if request.method == 'POST':
        formset = CustomArgsFormSet(country, user, request.POST, request.FILES)
        if formset.is_valid():
            # save validated forms
            return redirect('success')
        else:
            context["formset"] = formset
            return render(request, 'user/dashboard/view_template.html', context)
    else:
        context["formset"] = CustomArgsFormSet(country, user)
        return render(request, 'user/dashboard/view_template.html', context)

注意

需要注意的棘手的事情是使用自定义init的基础formset类的参数!参数需要作为非关键字传递(参见上面的视图)和request.POST之前,因为它们是未命名的args。他们也需要按照正确的顺序,首先是自定义参数,然后是request.POST等。

如果顺序错误,__init__方法将错误地映射参数,并且错误消息令人困惑。否则python不允许non-keyword arg after keyword arg

2 个答案:

答案 0 :(得分:1)

您只需为formset提供自定义 init 方法:

class BaseShippingTupleFormSet(BaseFormSet):
    def __init__(country_id, user, *args, **kwargs):
        self._country_id = country_id
        self._user = user
        super(BaseShippingTupleFormSet, self).__init__(*args, **kwargs)

然后在clean方法中使用self._country_id和self._user

答案 1 :(得分:1)

@evgeny的代码对我不起作用,但这确实有效:

class BaseShippingTupleFormSet(BaseFormSet):
    def __init__(self, country_id, user, *args, **kwargs):
        self._country_id = country_id
        self._user = user
        super().__init__(*args, **kwargs)