Flask和sqlalchemy:处理会话

时间:2016-02-27 01:03:42

标签: flask sqlalchemy

我最近开始在我的项目中使用Flask + Sqlalchemy,并在离开服务器一天后注意到500个错误。我认为这是由于数据库会话超时,但我不确定。我们是否应该为每个请求创建一个新会话,或者它是Flask应用程序启动的一对一? 我把它放在app.py

的顶部
from sqlalchemy import Column, ForeignKey, Integer, String, create_engine, func, cast, Float 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import relationship,scoped_session,sessionmaker,aliased
engine = createengine(DB_PATH) 
Session = sessionmaker(bind=engine) 
session = Session() 
app = Flask(name_)

然后对于视图中的所有查询,我执行类似的操作:“session.query(Table)......” 如果我为每个端点呼叫建立会话,这是错误的吗?

3 个答案:

答案 0 :(得分:5)

有些情况下使用Flask-SQLAlchemy Extension可能不合适。例如,如果您在完全不同的Python模块中管理模型类和数据库连接详细信息,以便在Flask之外的软件中重复使用,那么您不希望/需要扩展来为您管理这些内容。

假设您有自己的代码连接到数据库并通过类似的方式创建Session类(也假设提供了engine):

Session = scoped_session(sessionmaker(bind=engine))

对于需要数据库连接的页面,您可以使用该对象创建会话实例:

from flask import g

def my_page():
    session = Session()
    g.my_db_session = session # save session in the request context
    ...

我们需要删除已创建的会话。这需要在my_page函数结束后但在响应结束之前完成。为了这个目的,我们在线程本地g对象中保存会话。要在正确的时间删除它,请在创建Flask应用程序时添加以下代码:

    @app.teardown_appcontext
    def shutdown_session(exception=None):
       ''' Enable Flask to automatically remove database sessions at the
        end of the request or when the application shuts down.
        Ref: http://flask.pocoo.org/docs/patterns/sqlalchemy/
       '''
       if hasattr(g, 'my_db_session'):
           g.my_db_session.remove()

可能有其他方法可以做到这一点,但这是个主意。请注意,SQLAlchemy为您提供连接池。

答案 1 :(得分:3)

接受的答案有一些问题,尽管它应该有效。

  1. 它隐含地依赖于 threading.local()。虽然对于大多数应用程序都很好,但它忽略了安装 greenlet 的可能性,在这种情况下,本地线程 ID 是不够的。
  2. 它不必要地使用了 g。如评论中所述,scoped_session 已经处理了这部分。

Flask 本身不管理线程,这是 WSGI 服务器的职责。相应地,per the documentation 依赖线程范围不是存储数据库会话的推荐方式,尽管它应该可以正常工作,因为请求可能与线程直接相关。

<块引用>

特别是,虽然使用本地线程可能很方便,但最好将 Session直接与请求相关联,而不是与当前线程相关联。 因此,最好根据文档使用自定义范围,以便我们可以将会话直接与请求上下文相关联。这可以使用 custom created scope 来完成。

来自 SQLAlchemy 文档的伪代码

from my_web_framework import get_current_request, on_request_end
from sqlalchemy.orm import scoped_session, sessionmaker

Session = scoped_session(sessionmaker(bind=some_engine), scopefunc=get_current_request)

@on_request_end
def remove_session(req):
    Session.remove()

对于 SQLAlchemy,将会话附加到的最干净的对象似乎是应用程序上下文,因为这是与请求直接关联的最高级别变量。这是关于 Flask 上下文如何工作的 the flask documentation。您可以通过 LocalStack 使用 AppContext 实例访问内部 _app_ctx_stackThis stackoverlow answer 指向相同的解决方案。 _app_ctx_stack.__ident_func__ 函数很有用,因为它要么返回线程 ID,要么调用 greenlet 函数以提供可用标识符(如果已安装)。也就是说,烧瓶 does appear to use the thread local itself 很多东西。我搜索并搜索,但找不到任何可以保证 WSGI 服务器(例如 gunicorn 或 uwsgi)为每个请求创建线程的内容。如果有人有这方面的资料,我很乐意看到。无论如何,推荐的方法是使用应用程序上下文,这比依赖与请求具有相同生命周期的线程在语义上更清晰。

最后,另一条评论提到使用 Flask-SQLAlchemy。虽然这对大多数项目来说是个好主意,但我认为这并不总是有意义的。就个人而言,我希望使用 SQLAlchemy 定义我的模型定义,而不是通过 Flask-SQLAlchemy。我认为(就我而言)这些模型很可能在不久的将来会在 Flask 之外使用。我也不想使用与 SQLAlchemy 不同的 API。时期。虽然我认为它们即使不完全相同也可能非常相似,但它并没有使用我不喜欢的 SQLAlchemy 本身。我追溯地发现了一个得出相同结论的 blog from towardsdatascience

综上所述,我的解决方案与面向数据科学的人所做的几乎相同。我正在添加执行此操作的相关部分 from a repo they published

ma​​in.py

from flask import Flask, _app_ctx_stack
from sqlalchemy.orm import scoped_session
from .database import SessionLocal, engine

app = Flask(__name__)
app.session = scoped_session(SessionLocal, scopefunc=_app_ctx_stack.__ident_func__)

@app.teardown_appcontext
def remove_session(*args, **kwargs):
    app.session.remove()

database.py

  
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"

engine = create_engine(
    SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

主题非常复杂,所以我欢迎评论,我会更新答案,但希望这项研究对其他人有所帮助。

答案 2 :(得分:2)

我建议使用处理会话管理和连接池的伟大Flask SQLAlchemy Extension。此外,它还可根据请求等处理会话的打开和关闭。

您可以查看相关的SQLAlchemy文档以获取更多详细信息:http://docs.sqlalchemy.org/en/latest/orm/session_basics.html#session-frequently-asked-questions

来自文档:

  

一些Web框架包括基础结构,以协助将会话的生命周期与Web请求的生命周期保持一致。这包括与Flask Web框架结合使用的Flask-SQLAlchemy等产品,以及通常与Pyramid框架一起使用的Zope-SQLAlchemy。 SQLAlchemy建议使用这些产品。