【问题标题】:Only validate certain fields if a BooleanField is set如果设置了 BooleanField,则仅验证某些字段
【发布时间】:2010-12-21 02:41:09
【问题描述】:

场景:我正在构建一个订单。与地球上的所有其他订单一样,它具有单独的发票送货地址。我刚刚添加了一个“使用帐单邮寄地址”复选框以让用户节省时间。

问题是,运输字段仍然存在。如果用户不输入任何送货地址数据(例如,如果他们想使用帐单地址),它们将无法通过验证。

我想我想要覆盖这些重复字段的 ModelForm 验证。在那里,如果选中该框(不确定我如何从验证器中获取该数据),我将返回计费版本。如果未选中,我将其传递回原始验证。

听起来像是一个计划,不是吗?好吧,我在第一关就摔倒了。我的clean_functions 不工作。看起来他们甚至没有被召唤。

这里有一些代码:

# shipping_street is a field in my Order Model

class OrderForm(ModelForm):
    class Meta:
        model = Order

    def clean_shipping_street(self):
        print "JUST GET ME SOME OUTPUT!!!"
        raise forms.ValidationError('RAWRAWR')

这是我的测试方式:

def checkout(request):
    of = OrderForm()
    if request.method == "POST":
        of = OrderForm(request.POST)
        print 'Form valid:', of.is_valid()

    # ...
    # return my HttpResponse with 'of' in the context.

【问题讨论】:

    标签: django django-forms django-validation


    【解决方案1】:

    我不确定我是否只是个笨蛋,但以下方法有效(并回答了我的整个问题):

    类 OrderForm(ModelForm): 元类: 型号 = 订单

    def clean_shipping_street(self):
        print 'VALIDATING!!! YEY!'
        if self.cleaned_data['ship_to_billing']:
            return self.clean_billing_street()
        return super(OrderForm, self).clean_shipping_street()
    

    但是,如果您认为我的做法有误,请告诉我!

    正如尼克在下面指出的那样,cleaned_data 未按保证顺序填写,这意味着在调用clean_shipping_street()ship_to_billing 可能不存在。解决这个问题的方法是调用clean_shipping_street() 方法而不是访问cleaned_data

    def clean_shipping_street(self):
        print 'VALIDATING!!! YEY!'
        if self.clean_ship_to_billing():
            return self.clean_billing_street()
        return super(OrderForm, self).clean_shipping_street()
    

    如果您不像我在编写代码时那样懒惰,您可能希望避免对布尔字段进行如此多的重复验证。这应该会更快(前提是除非需要,否则默认字段不会运行 - 我自己不确定):

    def clean_shipping_street(self):
        print 'VALIDATING!!! YEY!'
        if self.cleaned_data.get('ship_to_billing', self.clean_ship_to_billing):
            return self.clean_billing_street()
        return super(OrderForm, self).clean_shipping_street()
    

    OR 甚至比这更好:

    def clean_shipping_street(self):
        if not self.cleaned_data.has_key['ship_to_billing']:
            self.cleaned_data['ship_to_billing'] = self.clean_ship_to_billing()
        if self.cleaned_data['ship_to_billing']:
            return self.clean_billing_street()
        return super(OrderForm, self).clean_shipping_street()
    

    这只是略有不同,但这应该意味着 clean_ship_to_billing() 的调用次数比我之前的努力少得多。但说真的,我怀疑你甚至可以在分析会话中检测到这些“改进”。

    【讨论】:

    • 我就是这样做的。也许不是最好的,但自定义清理方法可能是最简单的。
    • 请注意clean_<attribute-name> 方法不会以任何特定顺序调用,因此在上述情况下,ship_to_billing 可能尚未在cleaned_data 中设置。一般来说,如果您的 clean 方法依赖于表单中的多个项目,请使用普通的 clean 方法。
    • 我对尼克有个想法。而不是调用self.cleaned_data['field_name'],调用self.clean_field_name() - 这样你就可以保证结果(除非你的自定义清理有混乱的循环依赖)。 CPU 需要做更多的工作,但它应该始终有效。
    【解决方案2】:

    我的上一个答案有几个问题。复制的数据没有呈现回表单(可能是您想要的,我愿意),而且有点不可靠。

    这是我现在使用的。我没有添加几十个clean_field_name() 定义,而是在BooleanField 上添加了一个:

    def clean_ship_to_billing(self):
        if self.cleaned_data.get('ship_to_billing', False):
            data = self.data.copy()
            for f in ['street', 'street_2', 'post_code', 'city', 'county', 'country', ]:
                data['shipping_%s' % f] = data['billing_%s' % f]
            self.data = data
    

    如果选中,它会将帐单字段的原始数据复制到运输字段中。在模型(或表单)的字段顺序中,该字段位于运输字段之前,这一点很重要。

    我正在复制 self.data,因为 POST 数据是不可变的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-06
      • 2015-08-28
      • 1970-01-01
      • 2017-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多