过时数据烧瓶/SqlAlchemy 的问题



我有以下设置,在session.query((上SqlAlchemy返回过时的数据:

在带有 Gunicorn + supervisor 的 Flask 上运行的 Web 应用程序。 其中一个服务以这种方式组成:

  • app.py:

    @app.route('/api/generatepoinvoice', methods=["POST"])
    @auth.login_required
    def generate_po_invoice():
    try:
    po_id = request.json['po_id']
    email = request.json['email']
    return jsonify(response=POInvoiceGenerator.get_invoice(po_id, email))
    except Exception as ex:
    app.logger.error("generate_po_invoice(): " + ex.message)
    

在另一个文件夹中,我有与数据库相关的东西:

数据库模型(文件夹(

|-->Model.py

|-->Connection.py

这就是 connection.py 文件中包含的内容:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine(DB_BASE_URI, isolation_level="READ COMMITTED")
Session = scoped_session(sessionmaker(bind=engine))
session = Session()
Base = declarative_base()

这是 model.py 文件的摘录:

from DatabaseModels.Connection import Base
from sqlalchemy import Column, String, etc...
class Po(Base):
__tablename__ = 'PLC_PO'
id = Column("POId", Integer, primary_key=True)
code = Column("POCode", String(50))
etc...

然后我还有另一个文件 POInvoiceGenerator.py 包含对数据库的调用以获取一些数据:

import DatabaseModels.Connection as connection
import DatabaseModels.model as model
def get_invoice(po_code, email):
try:
po_code = po_code.strip()
PLCConnection.session.expire_all()
po = connection.session.query(model.Po).filter(model.Po.code == po_code).first()
except Exception as ex:
logger.error("get_invoice(): " + ex.message)

在随后的用户调用此服务时,有时我开始收到以下错误:无法在数据库中找到该特定代码的数据等等。就像数据是否过时等等。

我的第一个方法是将 isolation_level="READ COMMITTED" 添加到引擎声明中,然后创建一个作用域会话,但过时的数据读取不断应用。

有没有人知道我的设置是否错误(会话和模型在多个方法和文件中重复使用(

提前谢谢。

即使@TonyMountax指出的解决方案似乎有效,让我发现了一些我不知道的关于SqlAlchemy的东西,最终我选择了不同的东西。

我发现 SqlAlchemy 建立的连接是持久的,因为它每次都是从连接池创建的,这以某种方式导致数据过时。

我在代码中添加了一个空池:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.pool import NullPool
engine = create_engine(DB_URI, isolation_level="READ COMMITTED", poolclass=NullPool)
Session = scoped_session(sessionmaker(bind=engine))
session = Session()

然后,我为我所做的每个查询调用会话关闭:

session.query("some query..")
session.close()

这将导致 SqlAlchemy 每次都创建一个新连接并从数据库中获取新数据。

希望这是使用它的正确方法,并且可能对其他人有用。

实例化数据库连接的方式意味着它们被重用于下一个请求,并且它们在上一个请求中留下了一些状态。SQLAlchemy 使用会话的概念与数据库进行交互,这样即使您碰巧执行了两次相同的查询,您的数据也不会在单个请求中突然更改。当您使用 ORM 查询功能时,这是有意义的。例如,如果您在同一会话期间查询len(User.friendlist)两次,但在请求期间接受了好友请求,那么它仍将在两个位置显示相同的数字。

要解决此问题,您必须在第一个请求时设置会话,然后在请求完成后将其拆除。这样做并非易事,但已经有一个成熟的项目可以做到这一点:Flask-SQLAlchemy。它来自Pallets,Flask本身和Jinja2背后的人。

最新更新