Reputation: 183
I am attempting to display a list of checkboxes using wtforms.ext.sqlalchemy QuerySelectMultipleField, but I cannot get the model data to display on the form on a GET.
Here is my models.py
user_permissions = db.Table('user_permissions',
db.Column('permission_id', db.Integer, db.ForeignKey('permission.id')),
db.Column('user_id', db.Integer, db.ForeignKey('user.id'))
)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), nullable=False, unique=True)
email = db.Column(db.String(120), nullable=False, unique=True)
password = db.Column(db.String(80), nullable=False)
permissions = db.relationship('Permission', secondary=user_permissions,
backref=db.backref('permissions', lazy='dynamic'))
def __init__(self, username, email, password):
self.username = username
self.email = email
self.password = password
def __repr__(self):
return '<User %r>' % self.username
class Permission(db.Model):
id = db.Column(db.Integer, primary_key=True)
desc = db.Column(db.String(80), nullable=False)
def __init__(self, desc):
self.desc = desc
def __repr__(self):
return '<Permission %r>' % self.desc
Here is my forms.py
class EditUserForm(Form):
username = TextField('Name', validators=[DataRequired()])
email = TextField('Email', validators=[DataRequired()])
permissions = QuerySelectMultipleField(
query_factory=Permission.query.all,
get_label=lambda a: a.desc,
widget=widgets.ListWidget(prefix_label=False),
option_widget=widgets.CheckboxInput()
)
Here is my views.py
@app.endpoint('edit_user')
@require_login()
@require_permission('edit_user')
def edit_user(user_id):
user = User.query.filter_by(id=user_id).first()
if not user:
abort(404)
data = {
"username": user.username,
"email": user.email,
"permissions": user.permissions
}
form = EditUserForm(**data)
The problem, is that the WTForm doesn't display the selected values from user.permissions, which is a list of Permission models. It displays a list of empty checkboxes.
(Pdb) print user.permissions
[<Permission u'admin'>, <Permission u'create_user'>, <Permission u'edit_user'>]
I am fairly certain the issue lies within how the "permissions" data value is structured, but I have tried every possibility I can think of. Any help would be appreciated.
Some things I've tried...
"permissions": [1, 2]
"permissions": ['1', '2']
"permissions": ['permissions-1', 'permissions-2']
"permissions": [('permissions', 1), ('permissions', 2)]
"permissions": [('permissions', '1'), ('permissions', '2')]
Upvotes: 4
Views: 1855
Reputation: 16346
A couple of issues.
1) The way you did the query_factory could potentially cause an issue, as it potentially associates with a single session/query factory. This would mean the objects you're comparing to are not from the same session as the objects in the query factory.
A safer way is to do this:
query_factory=lambda: Permission.query.all()
2) I assume you're using Flask-WTF, since you didn't pass a formdata portion, but if you aren't (you're using wtforms.Form) then do this:
form = EditUserForm(request.form)
3) You passed a single-use iterator as a collection:
"permissions": user.permissions
If you really need to customize the passed in data via a dict, you should do it like this:
"permissions": list(user.permissions)
However, because of how basic your dict is, it's much preferred to simply pass the data as an object (and this should avoid the single-use iterator issue)
form = EditUserForm(obj=user)
Combining this all together, your view should look like this:
def edit_user(user_id):
user = User.query.filter_by(id=user_id).first()
if not user:
abort(404)
form = EditUserForm(request.form, user)
[...]
Upvotes: 3