使用Flask、WTForm、SQLAlchemy和Jinja2的完整多对一示例
这是我的HTML下拉菜单。这个值是子表的主键。
<select id="category" name="category">
<option selected value="__None"></option>
<option value="1">Category Number One</option>
<option value="2">Category Number Two</option>
</select>
我需要用整数1来更新Post.category_id,而不是用“类别编号一”。这是我的代码。
# 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,
body=form.body.data,
pub_date=form.pub_date.data,
cateogry_id=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)
我试过做...
cateogry_id=form.category.data
cateogry_id=form.category.value
那样不是很好吗!
1 个回答
8
我搞定了!这是我的解决方案。希望这个分享能帮助到下一个人。
解决办法就是让扩展来帮你完成工作!这里有一个使用 Flask、WTForm、SQLAlchemy 和 Jinja2 的 WT Forms sqlalchemy 扩展的示例。简单来说,你不需要担心子项的 ID,因为扩展会自动处理。这意味着当你在处理 SQLAlchemy 的父子模型(一个父项对应多个子项)时,你只需要关注父项就可以了。
模型
首先,确保你的模型和关系设置正确。注意我这个例子中关系是怎么定义的,模型的 init 里只有 CATEGORY,而不是 CATEGORY_ID。我之前的错误是以为我需要手动填充模型的 CATEGORY_ID 字段。其实不需要,扩展会为你处理这一切。事实上,如果你像我一样试图手动处理,那根本行不通……
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
其次,看看表单…… 注意我使用了 wtfrom sqlalchmey 的 QuerySelectedField,而 Category_ID 字段是缺失的……
表单
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)
现在是 FLASK 的路由和视图逻辑…… 注意在 POST 请求中,依然没有 category_id!只有 Category!
路由/视图
# 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)
最后是模板。你猜怎么着,我们只生成了 form.category 字段!!!
模板
<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>
结果是……这个解决方案正确地使用了 Category 模型作为查找表,并通过将 Category 的主键整数写入 Posts 的 Category_Id 字段,正确地关联了 Post 行。太棒了!