Flask-sqlalchemy:移动模型导致InvalidRequestError - 已经附加到会话

时间:2014-07-11 12:10:08

标签: flask sqlalchemy flask-sqlalchemy

将我的模型移动到他们自己的模块会导致InvalidRequestError ..这似乎与数据库会话和我在模型模块中导入db的方式有关。

我尝试将此问题减少到必要的最少量代码。首先,一个工作的Flask应用程序:

import os

from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SECRET_KEY'] = 'somesecret'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(app.instance_path, 'app.db')
db = SQLAlchemy(app)


class Thing(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    text = db.Column(db.String(300), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

    def __init__(self, user, text):
        self.user = user
        self.text = text


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    things = db.relationship('Thing', backref='user', lazy='dynamic')

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


@app.route('/', methods=['GET', 'POST'])
def add():
    if request.method == 'POST':
        username = request.form['username']
        text = request.form['txt']

        user = User.query.filter_by(username=username).first()
        thing = Thing(user, text)

        db.session.add(thing)
        db.session.commit()

    return render_template('index.html')

def initdb():
    db.create_all()
    db.session.add(User('test'))
    db.session.commit()

if __name__ == '__main__':
    app.run(debug=True)

索引模板,再次最小化:

<html>
  <head>
  </head>
  <body>
        <form method="post">
          <input type="text" name="username"></input>
          <input type="text" name="txt"></input>
          <button type="submit">Submit</button>
        <form>
  </body>
</html>

我们可以使用app.initdb()创建一个数据库,然后启动应用程序并在表单中输入一些数据(用户名应为&#39; test&#39;)。一个新项目被插入到Thing表中。尼斯:)

然后我将模型移动到models.py:

from app import db

class Thing(db.Model):
    ...

class User(db.Model):
    ...

新的app.py看起来像:

import os

from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SECRET_KEY'] = 'somesecret'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(app.instance_path, 'app.db')
db = SQLAlchemy(app)

#This line changes
import models


@app.route('/', methods=['GET', 'POST'])
def add():
    if request.method == 'POST':
        username = request.form['username']
        text = request.form['txt']

        # these 2 lines change: using models now
        user = models.User.query.filter_by(username=username).first()
        thing = models.Thing(user, text)

        db.session.add(thing)
        db.session.commit()

    return render_template('index.html')

def initdb():
    db.create_all()
    db.session.add(User('test'))
    db.session.commit()

if __name__ == '__main__':
    app.run(debug=True)

现在发布表格会给我这个错误:

sqlalchemy.exc.InvalidRequestError
InvalidRequestError: Object '<Thing at 0x11111b950>' is already attached to session '1' (this is '2')

当我调试这个时,有趣的是用户被连接到与我想要添加的会话不同的会话。显然db导入会导致模型类的数据库会话出错?谁能向我解释这里发生了什么?

顺便说一句,我可以通过更改Thing类的 init 并分配user_id而不是user来解决这个问题。这样,Thing实例不会从User实例复制会话。但这并不能解释为什么User实例的会话开始不正确。

1 个答案:

答案 0 :(得分:3)

您无意中导入了app.py模块两次。这里发生了什么:

  • 您输入$ python path/to/app.py
  • 解释器创建一个名为__main__的模块,并在其中执行app.py
  • 导入models,然后导入app
  • 没有名为app的模块,解释器创建一个名为app的模块并在其中执行app.py 第二次
  • 您的models.py正在使用app.db,但实际运行的烧瓶实例实际上正在使用__main__.db
修复它的最好方法是添加第三个模块,这样它就可以称为main,比如main_app.py,只包含:

import app
if __name__ == '__main__':
    app.app.run(debug=True)

并删除app.py中相应的if main子句。然后使用$ python path/to/main_app.py运行您的应用。