Skip to content

Instantly share code, notes, and snippets.

@ectrimble20
Created May 12, 2018 23:56
Show Gist options
  • Save ectrimble20/468156763a1389a913089782ab0f272e to your computer and use it in GitHub Desktop.
Save ectrimble20/468156763a1389a913089782ab0f272e to your computer and use it in GitHub Desktop.
Multiple Checkboxes using Flask WTForms
{% extends "layout_new.html" %}
{% block content_main %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">User Choices</legend>
{% if form.choices.errors %}
<div class="invalid-feedback">
{% for error in form.choices.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% endif %}
{% for choice in form.choices %}
<div class="form-check">
{{ choice(class="form-check-input") }}
{{ choice.label(class="form-check-label") }}
</div>
{% endfor %}
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content_main %}
class MultiCheckboxField(SelectMultipleField):
widget = widgets.ListWidget(prefix_label=False)
option_widget = widgets.CheckboxInput()
class ExampleForm(FlaskForm):
choices = MultiCheckboxField('Routes', coerce=int)
submit = SubmitField("Set User Choices")
@example.route("/example", methods=["GET", "POST"])
def example():
form = ExampleForm()
# populate the forms dynamically with the choices in the database
form.check_options.choices = [(c.id, c.desc) for c in Choices.query.all()]
# if it's a post request and we validated successfully
if request.POST and form.validate_on_submit():
# get our choices again, could technically cache these in a list if we wanted but w/e
c_records = Choices.query.all()
# need a list to hold our choices
accepted = []
# looping through the choices, we check the choice ID against what was passed in the form
for choice in c_records:
# when we find a match, we then append the Choice object to our list
if choice.id in form.check_options.data:
accepted.append(choice)
# now all we have to do is update the users choices records
user.choices = accepted
db.session.add(user)
db.session.commit(user)
else:
# tell the form what's already selected
form.choices.data = [c.id for c in user.choices]
return render_template('example.html', form=form)
user_choices = app_db.Table('user_choices',
app_db.Column('user_id', app_db.Integer, app_db.ForeignKey('user.id'), primary_key=True),
app_db.Column('choice_id', app_db.Integer, app_db.ForeignKey('choice.id'), primary_key=True)
)
class User(db.Model, UserMixin):
id = app_db.Column(app_db.Integer, primary_key=True)
# other stuff as required
choices = db.relationship('Choices', secondary=user_choices, lazy='subquery'
backref=db.backref('users_choice', lazy=True))
class Choices(db.Model):
id = app_db.Column(app_db.Integer, primary_key=True)
desc = app_db.Column(app_db.String(25), nullable=False)
@ectrimble20
Copy link
Author

Hi, thanks for the example. One thing I can't fully comprehend is: where did that form.check_options.choices come from? It looks like it should've been belong to the form.py but I can't quite figure out how. Could you please elaborate?

Had to go dig around in my old code to figure out what was going on here as I haven't touched this code in quite some time, I think check_options is a typo or a renaming that didn't make it throughout the example. I believe it's actually suppose to be form.choices.choices based on the ExampleForm class as it's suppose to be reference to the selected options from the forms MultiCheckboxField which is choices = MultiCheckboxField('Routes', coerce=int) in the example code. Thinking about it now, chances are when I was copying the code to make this little reference, I changed the name from choices.choices to check_options.choices because it looked weird with choices.choices.

A more complete example is in my FlaskProductSiteDemo repo on Github. The domain/admin/routes.py and domain/admin/forms.py has a clearer naming example that doesn't have a random name appear out of the blue.

@rushasdev
Copy link

I believe it's actually suppose to be form.choices.choices based on the ExampleForm class as it's suppose to be reference to the selected options from the forms MultiCheckboxField which is choices = MultiCheckboxField('Routes', coerce=int) in the example code. Thinking about it now, chances are when I was copying the code to make this little reference, I changed the name from choices.choices to check_options.choices because it looked weird with choices.choices.

I guess that makes sense, yeah. Thanks for the quick reply.

A more complete example is in my FlaskProductSiteDemo repo on Github. The domain/admin/routes.py and domain/admin/forms.py has a clearer naming example that doesn't have a random name appear out of the blue.

I'll definitely check it.

@ogost
Copy link

ogost commented May 18, 2020

Very useful, thank you!

@Nurou
Copy link

Nurou commented Jun 14, 2020

This was helpful. Thanks!

@cooneycw
Copy link

Can't say enough about how much this helped me! As a note to others, I had to import "widgets" from wtforms.

@evereux
Copy link

evereux commented Oct 11, 2020

# tell the form what's already selected
form.choices.data = [c.id for c in user.choices]

Thanks! 👍

@MarkhorMountain
Copy link

Very valuable. Thanks a lot!

@BonaFideBOSS
Copy link

How to set attributes (key and value) to each checkbox?

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