I am building a simple CMS that includes a profile management page. On this profile page, along with other fields, there exists a dropdown list, which I generate from a table 'parks' using the wtforms extension's QuerySelectField:
from wtforms.ext.sqlalchemy.fields import QuerySelectField
The rest of the fields on the profile page are populated by the 'user' table.
Let assume that a user has logged in, and that their record in the 'user' table is already populated. Among the fields in the 'user' table is a foreign key for a value from the 'parks' table: 'park_id'.
When a logged in user navigates to their profile page, I would like to display the selection in the dropdown that corresponds to this user's park_id foreign key value.
Note that Flask/Python et al is very new to me. Also, I am using Flask-Security and the User model below is based off their examples. Perhaps I need to build another relationship with users/parks?
Displaying a user's saved profile information, including that which might appear in a dropdown list to be made available for editing seems very basic to me. I fear I'm missing something fundamental. So far, other similar questions/answers on SO and elsewhere seem wildly confusing to me.
My User Model:
# Define models
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))
class Role(db.Model, RoleMixin):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='dynamic'))
park_id = db.Column(db.Integer)
weather_zip = db.Column(db.String(255))
flickr_api_key = db.Column(db.String(255))
flickr_api_secret = db.Column(db.String(255))
flickr_user_id = db.Column(db.String(255))
flickr_photo_set_id = db.Column(db.String(255))
My Parks Model:
class Parks(db.Model):
id = db.Column(db.Integer, primary_key=True)
park = db.Column(db.String(255))
My Profile Form (with query_factory thing)
def get_parks():
return Parks.query
class ProfileForm(Form):
id = HiddenField()
email = HiddenField()
park_id = QuerySelectField('Park',[validators.InputRequired(u'Please select a park')], default=19, query_factory=get_parks, get_label='park', allow_blank=True)
weather_zip = StringField('Zip Code',[validators.Length(min=5, max=5, message=u'Zip must contain 5 numbers'), validators.InputRequired(u'please enter a 5-digit zip code')])
flickr_api_key = StringField('Flickr API Key', [validators.InputRequired(u'Please supply a Flickr API key')])
flickr_api_secret = StringField('Flickr API Secret', [validators.InputRequired(u'Please supply a Flickr API secret')])
flickr_user_id = StringField('Flickr User ID', [validators.InputRequired(u'Please supply a Flickr user id')])
flickr_set_id = StringField('Flickr Set ID', [validators.InputRequired(u'Please supply a Flickr set id')])
My profile_page view:
@app.route('/profile/<id>', methods=["GET","POST"])
@login_required
def profile_page(id):
id = id
current_user = User.query.filter_by(id=id).first()
form = ProfileForm(obj=current_user)
return render_template("profile.html", form=form)
My template's form:
{% from "_formhelpers.html" import render_field %}
<form method=post action="/profile">
<dl>
{{ form.id(value=current_user.id) }}
{{ form.email(value=current_user.email) }}
{{ render_field(form.park_id) }}
{{ render_field(form.weather_zip) }}
{{ render_field(form.flickr_api_key) }}
{{ render_field(form.flickr_api_secret) }}
{{ render_field(form.flickr_user_id) }}
{{ render_field(form.flickr_set_id) }}
</dl>
<p><input type=submit value=Register>
</form>
A macro to render the fields (might be relevant?):
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs) |safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
Thanks for any help on all of this.
After drinking a couple beers and starring at this post: SQLAlchemy/WTForms: set default selected value for QuerySelectField until my eyes crossed, I discovered that I needed to pass a result object from a query to my parks table filtered by the current user's id.
A special detail is that I had to manually assign each field value (aside from the QuerySelectField, parks) from the current user's query result. This is because, in my case, simply passing the current_user result to the "obj=" arg would override my QuerySelectField keyword (in my case "park_id") because the "obj=current_user" assignment already contains its own instance of the park_id parameter, albeit one that doesn't work for displaying a default selection in my drop down.
So... The fix was to update my profile_page view function as follows:
@app.route('/profile/<id>', methods=["GET","POST"])
@login_required
def profile_page(id):
id = id
current_user = User.query.filter_by(id=id).first()
park = Parks.query.filter_by(id=current_user.park_id).first()
form = ProfileForm(park_id=park, # this is the main thing
weather_zip=current_user.weather_zip,
flickr_api_key=current_user.flickr_api_key,
flickr_api_secret=current_user.flickr_api_secret,
flickr_user_id=current_user.flickr_user_id,
flickr_photo_set_id=current_user.flickr_photo_set_id)
return render_template("profile.html", form=form)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With