Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not possible to have two forms of same class at the same time #284

Closed
abendebury opened this issue Jul 11, 2016 · 2 comments · Fixed by #286
Closed

Not possible to have two forms of same class at the same time #284

abendebury opened this issue Jul 11, 2016 · 2 comments · Fixed by #286
Assignees

Comments

@abendebury
Copy link

If you have a form like so:

class MultiCheckboxField(SelectMultipleField):
    widget = ListWidget(prefix_label=False)
    option_widget = CheckboxInput()

class ActivityListForm(Form):
    activities = MultiCheckboxField(validators=[DataRequired()], choices=[])
    submit = SubmitField("Submit")

    def populate_activities(self, activities_set):
        activities_mapping = {}
        for activity in activities_set:
            activities_mapping[str(activity.id)] = activity
            choice_tuple = (str(activity.id), activity.question)
            self.activities.choices.append(choice_tuple)
        return activities_mapping

And a flask view like so:

def settings_experiment(exp_id):
    experiment = Experiment.query.get(exp_id)

    remove_activities_form = ActivityListForm(prefix="remove")
    add_activities_form = ActivityListForm(prefix="add")

    remove_activities_mapping = remove_activities_form.populate_activities(
        experiment.activities)

    add_activities_mapping = add_activities_form.populate_activities(
        Activity.query.\
            filter(not_(Activity.experiments.any(id=experiment.id))).all())

    return render_template("experiments/settings_experiment.html",
                           experiment=experiment,
                           remove_activities_form=remove_activities_form,
                           add_activities_form=add_activities_form,
                           add_activities_mapping=add_activities_mapping,
                           remove_activities_mapping=remove_activities_mapping)

Then by the end of the view function, remove_activities_mapping.activities.choices == add_activities_mapping.activities.choices.

Essentially, it looks like two forms of the same class necessarily have the same choices for a field, which makes it hard to reuse a field on the same page.

@davidism
Copy link
Member

You've set the default choices to a mutable object, a list. WTForms stores the arguments passed to fields in order to construct bound fields later. This is the same situation as a mutable default argument in a normal Python function. Don't set a default, and instead initialize choices during __init__ (or populate_activities if it will always be called).

class F(Form):
    items = SelectField()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.items.choices = []

@davidism
Copy link
Member

davidism commented Jul 15, 2016

Patching SelectField.__init__ to do self.choices = copy(choices) seems reasonable, I'll do that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

2 participants