2015-11-30 20 views
5

Ho una vista Flask che utilizza SQLAlchemy per interrogare e visualizzare alcuni post del blog. Sto eseguendo la mia app usando mod_wsgi. Questa visualizzazione funziona la prima volta che vado alla pagina, ma la volta successiva restituisce un errore 500. Il traceback mostra l'errore ProgrammingError: SQLite objects created in a thread can only be used in that same thread. Perché sto ricevendo questo errore e come lo risolvo?Utilizzo della sessione SQLAlchemy da Flask solleva "Gli oggetti SQLite creati in una discussione possono essere utilizzati solo nella stessa discussione"

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 [{}] 

risposta

6

SQLAlchemy (e in questo caso anche SQLite) non funziona se si condivide una sessione attraverso i thread. Potresti non utilizzare i thread in modo esplicito, ma è mod_wsgi e hai definito un oggetto globale session. Utilizzare scoped_session per gestire la creazione di una sessione univoca per ogni thread.

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() 
    ... 

Preferibilmente, utilizzare Flask-SQLAlchemy che gestisce questo ed altre cose per voi. I documenti SQLAlchemy consigliano di utilizzare la libreria di integrazione invece di farlo da soli.

db = SQLAlchemy(app) 

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

Si noti inoltre che si dovrebbe essere definendo solo il motore, della sessione, ecc una volta e l'importazione altrove, invece di ridefinire in ogni file come il vostro codice attuale fa.

+0

Ha funzionato. In particolare, ho importato scopeed_session da sqlalchemy.orm, quindi ho creato un oggetto "SessionParent" come descritto in questa guida http://docs.sqlalchemy.org/en/latest/orm/contextual.html –

2

Prendendo spunto da this SO answer Ho cercato SA docs e scoperto che si può fare questo:

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

scoped_session non era proprio adatto nel mio caso, in quanto Flask-SQLAlchemy richiede solo un argomento stringa di connessione:

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) 
    ...