【问题标题】:WTForms: setting default value from an SQLAlchemy field with relationshipWTForms:从具有关系的 SQLAlchemy 字段中设置默认值
【发布时间】:2014-04-14 02:48:01
【问题描述】:

这里有很多关于 SO 的问题,其标题听起来与我将要描述的内容相似,但据我从字面上数小时的研究中可以看出,这个问题是独一无二的。就这样吧!

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

我的问题是,在编辑事务时,我不知道如何告诉 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 更加兼容?我有哪些选择?

谢谢!

【问题讨论】:

  • 是的,我正在处理similar issue。如何将关系的值传递给字段。你找到答案了吗?不过,在您的情况下,您似乎可以使用 SQLAlchemy WTForm 扩展,它将在下拉列表中显示类别值。你试过了吗?

标签: python sqlalchemy flask wtforms


【解决方案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.) 将 topic 添加到 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

【讨论】:

  • 谢谢,QuerySelectField 正是我想要的。我能够删除我编写的一堆现有代码(尝试)来做完全相同的事情。
猜你喜欢
  • 2019-06-21
  • 2014-03-02
  • 2016-11-10
  • 1970-01-01
  • 2018-05-23
  • 2021-04-07
  • 2013-07-11
  • 1970-01-01
  • 2012-08-19
相关资源
最近更新 更多