使用TypedChoiceField的FormSet在约每2000个请求后未强制转换为int

5 投票
1 回答
926 浏览
提问于 2025-04-17 19:13

我正在使用一个表单集合,这个集合里有几个表单,每个表单都有一个数量字段,定义如下:

quantity = TypedChoiceField(coerce=int, required=False)

我想知道至少有一个数量大于0,所以在我的表单集合的清理方法里,我写了这个:

def clean(self):
    if sum([form.cleaned_data['quantity'] for form in self.forms]) == 0:
        raise forms.ValidationError(_('No products selected'))

通常情况下,这样写是没问题的,form.cleaned_data['quantity']会是一个整数(因为我设置了coerce=int)。但是偶尔(大约每2000次请求中会出现一次),我会遇到一个异常,提示我:

TypeError: unsupported operand type(s) for +: 'int' and 'str'

在那一行,这意味着form.cleaned_data['quantity']变成了一个字符串,而sum()函数不喜欢把字符串相加,所以就抛出了异常。你可以自己测试一下,打开python控制台,输入:

>>> sum([u'1', u'2'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'unicode'
>>> 

所以我的问题是,为什么会发生这种情况?还有,为什么这种情况这么少见?django的文档告诉我,TypedChoiceField的强制转换是在调用clean()之前完成的,所以这不应该发生。

这个bug很难修复,因为很难重现,所以我希望你们中有人遇到过类似的问题。

这是在python 2.6和django 1.3.1上发生的。

提前感谢!

编辑 这是错误堆栈信息:

File "****/handlers/products.py" in process
  429.                if formset.is_valid():
File "/usr/local/lib/python2.6/dist-packages/django/forms/formsets.py" in is_valid
  263.        err = self.errors
File "/usr/local/lib/python2.6/dist-packages/django/forms/formsets.py" in _get_errors
  241.            self.full_clean() 
File "/usr/local/lib/python2.6/dist-packages/django/forms/formsets.py" in full_clean
   287.            self.clean()
File "****/handlers/products.py" in clean
  217.        if sum([form.cleaned_data['quantity'] for form in self.forms]) == 0:

异常类型:TypeError 在 /****/url 异常值:不支持的操作数类型:'int' 和 'str'

1 个回答

1

根据文档TypedChoiceField的默认empty_value是一个空字符串,而且这个值不会被转换。

我觉得你很可能偶尔会遇到空值,而导致你出现类型错误的那个字符串就是空字符串。你可以试试:

quantity = TypedChoiceField(coerce=int, required=False, empty_value=0)

撰写回答