Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FormSet using TypedChoiceField not coerced to int once every ~2000 requests

I'm using a FormSet that contains several forms that each have a quantity field which is defined like this:

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

I want to know whether at least one quantity > 0, so in my formset's clean, i write this:

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

So, normally this just works, and form.cleaned_data['quantity'] is an int (as set by coerce=int). But every once in a while (like once every 2000 requests for this form), i get an exception which tells me:

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

On that line, which means the form.cleaned_data['quantity'] is a string, and sum() doesn't like summing strings, so it throws an exception. You can test this yourself by starting the python console and typing:

>>> 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'
>>> 

So my question is, why does this happen? And also why does this happen so rarely? The django documentation tells me the coercing of the TypedChoiceField is guaranteed to be done before clean() is called, so this should not happen.

The bug is hard to fix because it's so hard to reproduce, so i hope one of you guys has had a problem similar to this.

This is on python 2.6 and django 1.3.1.

Thanks in advance!

EDIT So here is the stacktrace:

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:

Exception Type: TypeError at /****/url Exception Value: unsupported operand type(s) for +: 'int' and 'str'

like image 508
jaapz Avatar asked Nov 12 '22 09:11

jaapz


1 Answers

The default empty_value for a TypedChoiceField is the empty string, according to the docs, and that value is not coerced.

I think it's very likely that you're getting an empty value on occasion, and the string that's throwing your TypeError is the empty string. Try:

quantity = TypedChoiceField(coerce=int, required=False, empty_value=0)
like image 143
Hamms Avatar answered Nov 14 '22 21:11

Hamms