web-dev-qa-db-ja.com

FlaskからSQLAlchemyセッションを使用すると、「スレッドで作成されたSQLiteオブジェクトはその同じスレッドでのみ使用できます」が発生します。

Flaskビューがあり、SQLAlchemyを使用していくつかのブログ投稿をクエリおよび表示します。私はmod_wsgiを使用してアプリを実行しています。このビューは、ページに初めてアクセスしたときに機能しますが、500エラーを返します次回。トレースバックにエラーProgrammingError: SQLite objects created in a thread can only be used in that same thread.このエラーが発生するのはなぜですか。どうすれば修正できますか?

views.py

engine = create_engine('sqlite:////var/www/homepage/blog.db')
Base.metadata.bind = engine
DBSession = sessionmaker(bind = engine)
session = DBSession()

@app.route('/blog')
@app.route('/blog.html')
def blog():
    entrys = session.query(Entry).order_by(desc(Entry.timestamp)).all()
    return render_template('blog.html', blog_entrys = entrys)

models.py

class Entry(Base):
    __tablename__ = 'entry'

    id = Column(Integer, primary_key = True)

    title = Column(String(100), nullable = False)
    body = Column(String, nullable = False)
    timestamp = Column(DateTime, nullable = False)
    featured = Column(Boolean, nullable = False)

    comments = relationship('Comment')

    def is_featured(self):
        return self.featured


class Comment(Base):
    __tablename__ = 'comment'

    id = Column(Integer, primary_key = True)
    entry_id = Column(Integer, ForeignKey('entry.id'))

    text = Column(String(500), nullable = False)
    name = Column(String(80))


engine = create_engine('sqlite:////var/www/homepage/blog.db')
Base.metadata.create_all(engine)
Exception on /blog.html [GET]
Traceback (most recent call last):
  File "/usr/lib/python2.6/dist-packages/flask/app.py", line 861, in wsgi_app
    rv = self.dispatch_request()
  File "/usr/lib/python2.6/dist-packages/flask/app.py", line 696, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/var/www/homepage/webserver.py", line 38, in blog
    entrys = session.query(Entry).order_by(desc(Entry.timestamp)).all()
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/query.py", line 1453, in all
    return list(self)
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/query.py", line 1565, in __iter__
    return self._execute_and_instances(context)
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/query.py", line 1570, in _execute_and_instances
    mapper=self._mapper_zero_or_none())
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/session.py", line 735, in execute
    clause, params or {})
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1157, in execute
    params)
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1235, in _execute_clauseelement
    parameters=params
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1348, in __create_execution_context
    None, None)
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/engine/base.py", line 1343, in __create_execution_context
    connection=self, **kwargs)
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/engine/default.py", line 381, in __init__
    self.cursor = self.create_cursor()
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/engine/default.py", line 523, in create_cursor
    return self._connection.connection.cursor()
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/pool.py", line 383, in cursor
    c = self.connection.cursor(*args, **kwargs)
ProgrammingError: (ProgrammingError) SQLite objects created in a thread can only be used in that same thread.The object was created in thread id 140244498364160 and this is thread id 140244523542272 None [{}]
21
J. Pawelczyk

スレッド間でセッションを共有している場合、SQLAlchemy(この場合はSQLiteも)は機能しません。スレッドを明示的に使用していない可能性がありますが、mod_wsgi isであり、グローバルsessionオブジェクトを定義しました。 scoped_session スレッドごとに一意のセッションの作成を処理します。

session = scoped_session(sessionmaker(bind=engine))

@app.teardown_request
def remove_session(ex=None):
    session.remove()

@app.route('/')
def example():
    item = session.query(MyModel).filter(...).all()
    ...

できれば、これを処理する Flask-SQLAlchemy を使用してください。 SQLAlchemyのドキュメントでは、これを自分で行うのではなく、統合ライブラリを使用することをお勧めしています。

db = SQLAlchemy(app)

@app.route('/')
def example():
    item = db.session.query(MyModel).filter(...).all()
    ...

また、現在のコードのように各ファイルで再定義するのではなく、エンジンやセッションなどを1回だけ定義し、それを別の場所にインポートする必要があることにも注意してください。

21
davidism

this SO answer からヒントを得て、SA docsを検索し、これを実行できることがわかりました:

engine = create_engine('sqlite:////var/www/homepage/blog.db?check_same_thread=False')

scoped_session Flask-SQLAlchemyは接続文字列の引数しか取らないため、私の場合は実際には適していませんでした。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy


class Config(object):
    SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db?check_same_thread=False'


db = SQLAlchemy()


def create_app():
    app.config.from_object(Config)
    app = Flask(__name__)
    db.init_app(app)
    ...

による - sqlite3.connect

デフォルトでは、check_same_threadTrueであり、作成スレッドのみが接続を使用できます。 Falseを設定すると、返された接続が複数のスレッドで共有される可能性があります。 同じ接続で複数のスレッドを使用する場合、データの破損を防ぐために、書き込み操作をユーザーがシリアル化する必要があります。

30
reubano