Skip to content

Fix AttributeError in WizardView.post() when get_form_list() is overridden - #306

Open
TMBeaver wants to merge 1 commit into
jazzband:masterfrom
TMBeaver:fix-wizard-cache-invalidation-attributeerror
Open

TMBeaver wants to merge 1 commit into
jazzband:masterfrom
TMBeaver:fix-wizard-cache-invalidation-attributeerror

Conversation

@TMBeaver

@TMBeaver TMBeaver commented Sep 5, 2026 •

Copy link
Copy Markdown

The problem

A wizard that overrides get_form_list() without calling super() raises AttributeError and returns HTTP 500 on every successful step submission:

  File "formtools/wizard/views.py", line 317, in post
    del self._resolved_form_list
AttributeError: _resolved_form_list

Overriding get_form_list() is a supported extension point — the test suite already ships TestWizardWithCustomGetFormList for exactly this pattern:

class TestWizardWithCustomGetFormList(TestWizard):
    form_list = [Step1]

    def get_form_list(self):
        return {'start': Step1, 'step2': Step2}

The wizard renders fine on GET, but is unusable end-to-end: submitting step 1 crashes instead of advancing.

Root cause

The condition_dict caching added in 2.6 (#291) memoises on two attributes:

self._resolved_form_list = form_list
self._cache_signature = cache_signature

These are set only by the default WizardView.get_form_list(). post() then invalidates them with bare del statements, which raise when the attributes were never created:

# Clear caches as changed step data could affect conditions
del self._resolved_form_list
del self._cache_signature

The reader a few lines up already anticipates their absence:

if (hasattr(self, '_resolved_form_list') and
    self._cache_signature == cache_signature):

so the guard is simply missing on the invalidation side. This is a regression against 2.5.1, where post() had no cache-clearing code.

test_get_form_list_custom only issues a GET, which is why the suite stayed green.

The fix

Discard the caches only if they were set, matching the hasattr() guard the reader already uses. No behaviour change on the default path.

Tests

Added test_get_form_list_custom_post, a POST counterpart to the existing GET-only test_get_form_list_custom. It fails on master with the AttributeError above and passes with the fix.

Full suite: 100 passing before, 101 passing after.


🤖 Generated with Claude Code

…uper()

The condition_dict caching added in 2.6 stores its memo on the
_resolved_form_list and _cache_signature attributes, which are only ever
set by the default WizardView.get_form_list(). WizardView.post() cleared
that memo with bare del statements, so a wizard that overrides
get_form_list() without delegating to super() raised AttributeError and
returned HTTP 500 on every successful step submission.

Overriding get_form_list() is a supported extension point, and the test
suite already ships TestWizardWithCustomGetFormList for it, but the
existing test only issues a GET, so the crash went unnoticed.

Discard the caches only if they were set, matching the hasattr() guard
the reader already uses, and add a POST regression test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant