我最近开始在我的项目中使用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(表)...”这是不是错了,我应该为每个端点调用建立一个会话吗?
发布于 2021-01-28 03:10:40
公认的答案有几处错误,尽管无可否认,它应该是有效的。
threading.local()
。虽然对于大多数应用程序来说都很好,但它忽略了安装greenlet
的可能性,在这种情况下,本地线程ID是不够的。g
。正如评论中指出的那样,scoped_session
已经处理了这一部分。Flask本身不管理线程,这是WSGI服务器的责任。适当地,依赖线程作用域的per the documentation不是存储db会话的推荐方式,尽管它应该工作得很好,因为请求很可能直接与线程相关联。
尤其是,虽然使用线程本地可能很方便,但最好将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。您可以使用AppContext实例通过_app_ctx_stack
访问内部LocalStack。This stackoverlow answer指出了同样的解决方案。_app_ctx_stack.__ident_func__
函数非常有用,因为它要么返回线程id,要么调用一个greenlet函数来给出一个可用的标识符。也就是说,flask does appear to use the thread local itself可以做很多事情。我找了又找,但没有找到任何可以保证WSGI服务器为每个请求创建线程的东西。如果谁有这方面的消息来源,我很乐意看看。无论如何,推荐的方法是使用应用程序上下文,这在语义上比依赖与请求具有相同生命周期的线程更清晰。
最后,另一条评论提到使用Flask-SQLAlchemy。虽然这对于大多数项目来说是个好主意,但我不认为它总是有意义。就我个人而言,我希望我的模型定义是用SQLAlchemy定义的,而不是通过Flask-SQLAlchemy。我认为在不久的将来,这些模型很可能(在我的情况下)将在Flask之外使用。我也不想要一个与SQLAlchemy不同的API。句号。虽然我认为它们很可能非常相似,如果不是完全相同的话,但它没有使用SQLAlchemy本身,这是我不喜欢的。我回溯发现了一个得出相同结论的blog from towardsdatascience。
尽管如此,我的解决方案看起来与未来的数据科学人员所做的几乎相同。我添加了相关的部分from a repo they published来做这件事。
main.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()
这个主题get相当复杂,所以我欢迎评论,我会更新答案,尽管希望这项研究对其他人有所帮助。
https://stackoverflow.com/questions/35664436
复制相似问题