I get it! Here is my solution. Hope this post helps the closest guy.
The solution is to let the extension do the work for you! The following is a working example of a sqlalchemy WT Forms extension using Flask, WTForm, SQLAlchemy, and Jinja2. In short, you donβt have to worry about the child identifier, because the extension will take care of this automatically. this means that when you work with SQLAlchemy Parent and Child in a one-to-many relationship, you ONLY have to deal with PARENT.
MODEL
First, make sure your model and relationships are correct. Note in my example how relationships are defined and that in the init model there is only CATEGORY ... NOT CATEGORY_ID. My mistake was that I would fill in the CATEGORY_ID model field. Nope. The extension does this for you. In fact, if you try to do it manually, like me, it will not work at all.
class Post(db.Model): id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(80)) url = db.Column(db.String(120)) body = db.Column(db.Text) create_date = db.Column(db.DateTime) pub_date = db.Column(db.DateTime) pub_status = db.Column(db.Text(80)) author_id = db.Column(db.Integer, db.ForeignKey('user.id')) author = db.relationship('User', backref=db.backref('posts', lazy='dynamic')) category_id = db.Column(db.Integer, db.ForeignKey('category.id')) category = db.relationship('Category', backref=db.backref('posts', lazy='dynamic')) def __init__(self, title, body, category, pub_date=None): self.title = title self.body = body if pub_date is None: pub_date = datetime.utcnow() self.category = category self.pub_date = pub_date def __repr__(self): return '<Post %r>' % self.title class Category(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50)) description = db.Column(db.String(250)) def __init__(self, name): self.name = name def __repr__(self): return self.name
Secondly, check the form .... ATTENTION I am using wtfrom sqlalchmey QuerySelectedField and the Category_ID field is missing ...
THE FORM
from sprucepress.models import Tag, Category from flask_wtf import Form from wtforms.fields import StringField, DateTimeField from wtforms.widgets import TextArea from wtforms.validators import DataRequired from wtforms.ext.sqlalchemy.orm import model_form from wtforms.ext.sqlalchemy.fields import QuerySelectField def enabled_categories(): return Category.query.all() class PostForm(Form): title = StringField(u'title', validators=[DataRequired()]) body = StringField(u'Text', widget=TextArea()) pub_date = DateTimeField(u'date create') category = QuerySelectField(query_factory=enabled_categories, allow_blank=True)
Now the logic for routing and viewing FLASK ... Pay attention to the post NO category_id again! Only category !!!
GASKET / VIEW
# create new post @app.route('/admin/post', methods=['GET', 'POST']) @login_required # required for Flask-Security def create_post(): form = PostForm() if form.validate_on_submit(): post = Post(title=form.title.data, pub_date=form.pub_date.data, body=form.body.data, category=form.category.data) db.session.add(post) db.session.commit() flash('Your post has been published.') return redirect(url_for('admin')) posts = Post.query.all() return render_template('create_post.html', form=form, posts=posts)
Finally, the template. Guess what, we only generate the form.category field !!!
TEMPLATE
<form action="" method=post> {{form.hidden_tag()}} <dl> <dt>Title: <dd>{{ form.title }} <dt>Post: <dd>{{ form.body(cols="35", rows="20") }} <dt>Category: <dd>{{ form.category }} </dl> <p> <input type=submit value="Publish"> </form>
Result ... this solution correctly uses the category model as a lookup table and correctly binds Post columns by writing PK integers to the Posts Category_Id field. Yeeeehaww!