【问题标题】:Updating a record with WTForms, SQLAlchemy & Flask使用 WTForms、SQLAlchemy 和 Flask 更新记录
【发布时间】:2018-07-13 13:01:07
【问题描述】:

每当我使用 WTForms 编辑表单时,它不会更新记录,而是将其添加为新记录。

我已经在 Flaskr 示例应用程序中复制了这个,所以它一定是我做错了,但我不确定是什么。

我在这里下载了 Flaskr:https://github.com/lepture/flask-wtf/tree/master/examples/flaskr

这是一个简单的表单,有两个输入字段 - 标题和文本。可以提交表单,并将条目保存到 sqlite 数据库。

我尝试修改它以允许更新条目。

class Entry(db.Model):
    __tablename__ = "entries"
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.Unicode(200))
    text = db.Column(db.UnicodeText)

class EntryForm(FlaskForm):
    title = TextField("Title", validators=[DataRequired()])
    text = TextAreaField("Text")
    submit = SubmitField("Share")

@app.route('/add', methods=['POST'])
def add_entry():
    if not session.get('logged_in'):
        abort(401)

    form = EntryForm()
    if form.validate():
        entry = Entry()
        form.populate_obj(entry)
        # entry.id=1
        db.session.add(entry)
        db.session.commit()
        flash('New entry was successfully posted')
    else:
        flash("Your form contained errors")

    return redirect(url_for('show_entries'))

我添加了一种新方法来使用现有条目填充表单:

@app.route('/edit/<int:id>', methods=['GET'])
def edit_entry(id=None):
    entries = Entry.query.order_by(Entry.id.asc())
    entry = Entry.query.get(id)
    form = EntryForm(obj=entry)
    return render_template('show_entries.html', entries=entries, form=form)

然后我通过表单添加了一个新条目并导航到http://127.0.0.1:5000/edit/1,它确实使用我刚刚创建的条目填充了表单。

但是,当重新发布表单时,它不会更新数据库中的现有记录,而是插入一条新记录。我想可能是因为 ID 未在“条目”对象中设置,但我什至在保存它之前手动将其设置为 1,它仍然尝试进行插入(并且使 PK 约束失败)而不是更新。

【问题讨论】:

    标签: python flask sqlalchemy wtforms


    【解决方案1】:

    这是一个老问题,但这是我使用 Flask-SQLAlchemy 的方法。这些路由是分开的,但它们都调用同一个模板以避免重复。

    Form 可以将对象作为参数,并填充表单字段以进行“更新”。然后在表单上调用populate_obj 方法来执行此操作,更改对象以反映新的表单字段。现在可以将新实例添加到数据库中。

    @app.route("/add_item", methods=['GET', 'POST'])
    def add_item():
    
        form = AddItem()
    
        if form.validate_on_submit():
            """Get instance of version from form data."""
            item = Item(field1=form.field1.data,
                        field2=form.field2.data,
                        field3=form.field3.data,
                        )
            db.session.add(item)
            db.session.commit()
    
        return render_template('add_item.html', form=form)
    
    
    @app.route("/edit_item/<item_id>", methods=['GET', 'POST'])
    def edit_item(item_id):
    
        item = db.session.query(Item).get(item_id)
    
        form = AddItem(obj=item)
        if form.validate_on_submit():
            form.populate_obj(item)
            db.session.add(item)
            db.session.commit()
            return redirect(url_for('home'))
    
        return render_template('add_item.html', form=form)
    

    【讨论】:

      【解决方案2】:

      首先,POST 到 /add 将始终插入一个新条目,根据您的代码判断。更新和插入应该发生在不同的端点中。

      SQLAlchemy 在后端和数据库之间提供对象关系映射。这意味着,SQLAlchemy 不仅会看到您正在制作的那些新构建的表单与您已经插入到数据库中的表单不同(尽管它们的所有字段都是相同的),而且您对映射对象的字段反映在该对象的数据库条目中。

      您可以简单地通过从表中查询对象、将对象的字段设置为其更新值并调用db.session.commit() 来更新对象。比如:

          # Assuming you're using flask-sqlalchemy
          form = Entry.query.get(id)
          form.title = 'Fred Flinstone'
          form.text = 'yabba dabba doo'
          db.session.commit(form)
      

      【讨论】:

      • 感谢您的回复。但是在编辑后发布预先填充的表单时,ID不会传递?所以我无法从数据库中提取记录并从表单数据中更新它。我还认为使用 form.populate_obj(entry) 直接从表单加载条目对象是一种好习惯?我正在努力实现这样的目标。 goonan.io/flask-wtf-tricks/amp,其中 New 和 Edit 以相同的方法处理。谢谢
      • 如果您希望 Naw 和 Edit 以相同的方法处理,那么为什么您的示例显示它们每个都有自己的方法?
      • Edit_entry 使用传入其 ID 的对象填充 EntryForm。表单始终发布到 add_entry。我不太在意它们是否是相同的方法 - 我只是希望它会识别出它是同一个对象(至少如果它具有相同的主键)。这可能是因为我使用的是普通的 SQLAlchemy,而我看到的示例是针对 Flask-SQLAlchemy 的,它按照我的理解以不同的方式处理会话。从那以后,我发现了 Flask-Admin,它可以立即完成我需要的一切,但仍然有兴趣看看这里是否有一个优雅的解决方案
      【解决方案3】:

      一个具体的例子:

      class Article(UserMixin, db.Model):
          id = db.Column(db.Integer, primary_key=True)
          name = db.Column(db.String(64), unique=True, nullable=False)
          # price
          # stock
          category = db.Column(db.String(64))
          sub_category = db.Column(db.String(64))
          description = db.Column(db.Text())
      
      class ArticleForm(FlaskForm):
          name = StringField('Name', render_kw={"placeholder": "Nombre del producto"}, validators=[
                             DataRequired(), Length(max=64)])
          category = StringField('Category', validators=[
                                 DataRequired(), Length(max=64)])
          sub_category = StringField(
              'sub-category', validators=[DataRequired(), Length(max=64)])
          description = TextAreaField('Description', render_kw={"rows": 10, "cols": 30}, validators=[
              DataRequired()])
      
      @app.route('/update_article/<id>', methods=['GET', 'POST'])
      def update_article(id):
      
          article = Article.query.get(id)
      
          form = ArticleForm(obj=article)
          if form.validate_on_submit():
              form.populate_obj(article)
              db.session.commit()
              return redirect(url_for('index')) 
      
          return render_template("add_article.html", form=form)
      

      和add_article.html:

      {% extends "base.html" %}
      
      {% block title %}
      Edit Article
      {% endblock %}
      
      {% block styles %}
      {{super()}}
      <link rel="stylesheet" href="{{url_for('static', filename='css/login.css')}}">
      {% endblock %}
      
      {% block content %}
      <div class="global-container">
          <div class="card login-form">
              <div class="card-body">
                  <h3 class="card-title text-center"><b>Edit Article</b></h3>
                  <div class="card-text">
                      {% with messages = get_flashed_messages() %}
                          {% if messages %}
                              <div class="alert alert-danger">
                                  {{ messages[0] }}
                              </div>
                          {% endif %}
                      {% endwith %}
                      <form action="" method="post" novalidate>
                          {{ form.hidden_tag() }}
                          <div class="form-group">
                              {{ form.name.label }}<br>
                              {{ form.name }}<br>
                              {% for error in form.name.errors %}
                              <span style="color: red;">{{ error }}</span>
                              {% endfor %}
                          </div>
                          <div class="form-group">
                              {{ form.category.label }}<br>
                              {{ form.category }}<br>
                              {% for error in form.category.errors %}
                              <span style="color: red;">{{ error }}</span>
                              {% endfor %}
                          </div>
                          <div class="form-group">
                              {{ form.sub_category.label }}<br>
                              {{ form.sub_category }}<br>
                              {% for error in form.sub_category.errors %}
                              <span style="color: red;">{{ error }}</span>
                              {% endfor %}
                          </div>
                          <div class="form-group">
                              {{ form.description.label }}<br>
                              {{ form.description }}<br>
                              {% for error in form.description.errors %}
                              <span style="color: red;">{{ error }}</span>
                              {% endfor %}
                          </div>
                          <div class="form-group">
                              {{ form.submit() }}
                          </div>
                      </form>
                  </div>
              </div>
          </div>
      </div>
      {% endblock %}
      

      【讨论】:

        猜你喜欢
        • 2014-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-15
        • 2016-10-16
        • 2018-09-06
        • 1970-01-01
        • 2020-06-22
        相关资源
        最近更新 更多