Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing django inline formsets

In attempting to programmatically post a new ForeignKey object through an inline formset, I'm receiving an error: ValueError: invalid literal for int() with base 10: ''.

Here's the code of my test (bloated for the sake of brevity here):

def test_merits_can_be_added(self):
    self.c = Client()
    self.c.login(username=self.user.username, password='dummy')
    self.post_data = {
      'name':u'Unhappy SE',
      'concept':u'Sad clown',
      'merit-TOTAL_FORMS':u'1',
      'merit-MAX_NUM_FORMS':u'',
      'merit-INITIAL_FORMS':u'1',
      'merit-0-id':u'',
      'merit-0-level':u'2',
      'merit-0-character':u'1',
      'merit-0-trait':u'11',
      'merit-0-specializations':u'Sometimes'
    }
    sheet = GeistCharacterSheet.objects.create(name='Happy SE', user=self.user)
    response = self.c.post(sheet.get_absolute_url(), self.post_data, follow=True)
    self.assertEqual(GeistCharacterSheet.objects.get(pk=1).chosentrait_set.all().filter(trait__name='Common Sense')[0].level, 2)
    self.assertEqual(GeistCharacterSheet.objects.get(pk=1).chosentrait_set.all().filter(trait__name='Common Sense')[0].specializations, u'Sometimes')

The view code (again, trimmed for brevity):

def character_sheet(request, sheet_id=None):
  charsheet = GeistCharacterSheet.objects.get(pk=sheet_id, user=request.user)
  if request.method == 'POST':
    sheet_form = GeistCharacterSheetForm(request.POST, instance=charsheet)          
    merit_formset = setup_merit_form(charsheet, post=request.POST)

    if sheet_form.is_valid() and merit_formset.is_valid():
      sheet_form.save()
      merit_formset.save()
      return redirect('/character-manager/list/')

def setup_merit_form(charsheet, post=None):
  MeritFormSet = inlineformset_factory(GeistCharacterSheet, ChosenTrait, form=ChosenMeritForm, extra=1)
  if post:
    return MeritFormSet(post, instance=charsheet, queryset=ChosenTrait.objects.filter(trait__trait_type__name='Merit'), prefix='merit')
  else:
    return MeritFormSet(instance=charsheet, queryset=ChosenTrait.objects.filter(trait__trait_type__name='Merit'), prefix='merit')

Here's the traceback from the test execution:

Traceback (most recent call last):
  File "C:\charon_sheet\..\charon_sheet\character_manager\tests.py", line 119, in test_skills_can_be_changed
    response = self.c.post(sheet.get_absolute_url(), self.post_data, follow=True)
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\test\client.py", line 449, in post
    response = super(Client, self).post(path, data=data, content_type=content_type, **extra)
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\test\client.py", line 259, in post
    return self.request(**r)
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\core\handlers\base.py", line 111, in get_response
    response = callback(request, *callback_args, **callback_kwargs)
  File "C:\charon_sheet\..\charon_sheet\character_manager\views.py", line 29, in character_sheet
    merit_formset = setup_merit_form(charsheet, post=request.POST)
  File "C:\charon_sheet\..\charon_sheet\character_manager\views.py", line 69, in setup_merit_form
    return MeritFormSet(post, instance=charsheet, queryset=ChosenTrait.objects.filter(trait__trait_type__name='Merit'), prefix='merit')
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\forms\models.py", line 682, in __init__
    queryset=qs)
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\forms\models.py", line 415, in __init__
    super(BaseModelFormSet, self).__init__(**defaults)
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\forms\formsets.py", line 47, in __init__
    self._construct_forms()
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\forms\formsets.py", line 108, in _construct_forms
    self.forms.append(self._construct_form(i))
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\forms\models.py", line 691, in _construct_form
    form = super(BaseInlineFormSet, self)._construct_form(i, **kwargs)
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\forms\models.py", line 437, in _construct_form
    connection=connections[self.get_queryset().db])
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\db\models\fields\subclassing.py", line 53, in inner
    return func(*args, **kwargs)
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\db\models\fields\subclassing.py", line 53, in inner
    return func(*args, **kwargs)
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\db\models\fields\__init__.py", line 306, in get_db_prep_lookup
    value = self.get_prep_lookup(lookup_type, value)
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\db\models\fields\__init__.py", line 292, in get_prep_lookup
    return self.get_prep_value(value)
  File "C:\Program Files\BitNami DjangoStack\apps\django\django\db\models\fields\__init__.py", line 479, in get_prep_value
    return int(value)
ValueError: invalid literal for int() with base 10: ''

I can post models, forms, more of the view, whatever anyone thinks would be helpful.

The issue is with the 'merit-0-id':u'' post item. I've tried with and without unicode, using 0 or -1, using False, using 'new' (a complete wild shot, I know).

My major confusion comes in that the form works when I'm running the server. I've examined the POST variables when I submit the form, and that id field can be empty and the item is added just fine.

Why is the form balking when it's submitted via a test runner?

like image 290
Melissa Avery-Weir Avatar asked Mar 01 '12 16:03

Melissa Avery-Weir


1 Answers

Initial forms in an inline formset need to tie back to existing models in the DB. Your setup doesn't create the related ChosenTrait instance (which is the pk you should be using for merit-0-id). If you are testing creating all new models then 'merit-INITIAL_FORMS' should be 0.

like image 101
Mark Lavin Avatar answered Nov 17 '22 08:11

Mark Lavin