WTForms:从具有关系的SQLAlchemy字段设置默认值

时间:2014-04-14 02:48:02

标签: python sqlalchemy flask wtforms

这里有很多关于SO的问题,其标题听起来与我将要描述的类似,但据我从几个小时的研究中可以看出,这个问题是独一无二的。所以这里去了!

我正在编写我的第一个Flask应用程序。我正在使用SQLAlchemy作为模型层,使用WTForms来处理表单。该应用程序将成为一个轻量级的个人财务经理,我可能不会真正用于严肃的商业。我有一个表用于所有交易的列表,另一个表用于所有费用类别(杂货,服装等)。事务表有一个列(“类别”),它引用了Category表。在视图中,我表示带有元素的类别列表。

我的问题是,在编辑事务时,我无法弄清楚如何告诉WTForms将元素设置为特定的预定义值。 (是的,我知道您可以在定义表单时设置默认值,这不是我要求的。)

模型看起来像这样(删除了不相关的字段):

class Category(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False, unique=True)
    # ...

class Trans(db.Model):
    # ...
    category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
    category = db.relationship('Category',
                                backref=db.backref('trans', lazy='dynamic'))
    # ...

forms.py:

def category_choices():
    return [('0', '')] + [(c.id, c.name) for c in Category.query.all()]

class TransactionForm(Form):
    # ...
    category = SelectField('Category', coerce=int, validators=[InputRequired()])
    # ...

路线(POST尚未实施):

@app.route('/transactions/edit/<trans_id>', methods=['GET', 'POST'])
def trans_edit(trans_id):
    transaction = Trans.query.get(trans_id)
    form = forms.TransactionForm(obj=transaction)
    form.category.choices = forms.category_choices()
    form.category.default = str(transaction.category.id)
    #raise Exception # for debugging
    return render_template('trans.html',
                           title='Edit Transaction',
                           form=form)

最后,模板(Jinja2):

{{ form.category(class='trans-category input-medium') }}

正如您在路径中看到的,我从transaction.category.id设置了form.category.default,但这不起作用。我认为我的问题是我在创建表单后设置了“默认”。因为模型来自SQLAlchemy的数据库,所以我不得不这样做。根本原因似乎是form.category是一个对象(由于关系),WTForms似乎无法轻易处理。我不可能是第一个遇到这个问题的人......我是否需要重新设计模型以使WTForms更兼容?我有什么选择?

谢谢!

1 个答案:

答案 0 :(得分:1)

我在评论中提到了这一点。听起来您可能会从使用WTForm的SQLAlchemy扩展中受益。这将为您的转换表单中的类别创建一个下拉列表。

我的示例用例略有不同。我将博客文章与主题联系起来。也就是说,许多帖子共享一个主题。在你的情况下我想象,许多交易共享一个类别。

<强>表格

from wtforms.ext.sqlalchemy.fields import QuerySelectField  #import the ext.

def enabled_topics(): # query the topics (a.k.a categories)
    return Topic.query.all()

class PostForm(Form):  # create your form
    title = StringField(u'title', validators=[DataRequired()])
    body = StringField(u'Text', widget=TextArea())
    topic = QuerySelectField(query_factory=enabled_topics, allow_blank=True)

<强>模型 这里的重要部分是a)确保正确定义了关系,并且b。)将主题添加到init,因为您使用它来创建新条目。

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(80))
    body = db.Column(db.Text)
    # one-to-many with Topic
    topic = db.relationship('Topic', backref=db.backref('post', lazy='dynamic'))

def __init__(self, title, body, topic):
        self.title = title
        self.body = body
        self.topic = topic

class Topic(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))

    def __init__(self, name):
        self.name = name

查看 这里没什么特别的。只是一个生成表单并处理提交结果的常规视图。

@app.route('/create', methods=['GET', 'POST'])
@login_required
def create_post():
    form = PostForm()
    if form.validate_on_submit():
        post = Post(title=form.title.data, body=form.body.data,
                    topic=form.topic.data)
        db.session.add(post)
        db.session.commit()
        Topic.update_counts()
        flash('Your post has been published.')
        return redirect(url_for('display_post', url=url))
    posts = Post.query.all()
    return render_template('create_post.html', form=form, posts=posts)

<强>模板 这里没什么好看的。只需确保在模板中渲染字段,就像在基本字段中一样。由于WTForms Sqlalchemy扩展功能可以满足您的需求,因此无需花哨的循环。

{% extends "base.html" %}
{% block title %}Create/Edit New Post{% endblock %}
{% block content %}
<H3>Create/Edit Post</H3>
<form action="" method=post>
   {{form.hidden_tag()}}
   <dl>
      <dt>Title:
      <dd>{{ form.title }}
      <dt>Post:
      <dd>{{ form.body(cols="35", rows="20") }}
      <dt>Topic:
      <dd>{{ form.topic }}
   </dl>
   <p>
      <input type=submit value="Publish">
</form>
{% endblock %}

那就是它!现在我的帖子表单有一个主题下拉列表。要使用您的术语,在加载事务时,该事务的默认类别将在下拉列表中突出显示。说明这一点的正确方法是通过trans模型中定义的关系加载与事务关联的类别。

另请注意,如果一个事务有多个默认值,那么还有一个多选SQLAlchemy扩展名。类别。

现在,我的问题是如何处理多对多关系....我试图将存储在多对多表中的一串标记传递给TextArea字段。没有SQLAlchemy扩展!

我发布了这个问题here