使用全局数据库实例的 Flask-SQLAlchemy

Flask-SQLAlchemy using global DB instance

这是我所做的:

    from flask import Flask
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker

    dbhost = 'localhost'
    dbuser = 'user'
    dbpass = 'password'
    dbname = 'db'
    DBUri = 'mysql://%s:%s@%s/%s?charset=utf8'%(dbuser,dbpass,dbhost,dbname)

    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = (DBUri)
    app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'


    # an Engine, which the Session will use for connection
    # resources
    engine = create_engine(DBUri)

    # create a configured "Session" class
    Session = sessionmaker(bind=engine)

    # create a Session
    DBsession = Session()

@sched.cron_schedule(second='*/5')
def some_decorated_task():
    date_now = datetime.datetime.now().date()
    for item in DBsession.query(user_groups_n_hosts).filter(user_groups_n_hosts.end_time < str(date_now)): 
        print item.id, item.server_users.native_linux_user

有一个tableuser_groups_n_hosts上面的代码是一个函数,每30秒运行一次

我的问题是一旦我启动我的 Flask 应用程序,如果 user_groups_n_hosts 有 10 条记录,那么上面的代码将继续打印 10 条记录,无论如何从外部插入 table user_groups_n_hosts 的记录数。换句话说,我认为我的函数有一些 table 实例,它只在应用程序初始化开始时被初始化。 我知道我在做一些愚蠢的事情。请有人指出。

代替DBsession.query(user_groups_n_hosts).filter(user_groups_n_hosts.end_time < str(date_now)): 我试过了 user_groups_n_hosts.query.filter(user_groups_n_hosts.end_time < str(date_now)): 但是结果是一样的

您的过滤器 user_groups_n_hosts.end_time < str(date_now) 将日期与字符串进行比较。

此外,您应该将会话视为每个请求的事物,而不是应用程序中的持久实体。

考虑到这一点,尝试更改

@sched.cron_schedule(second='*/5')
def some_decorated_task():
    date_now = datetime.datetime.now().date()
    for item in DBsession.query(user_groups_n_hosts).filter(user_groups_n_hosts.end_time < str(date_now)): 
        print item.id, item.server_users.native_linux_user

@sched.cron_schedule(second='*/5')
def some_decorated_task():
    date_now = datetime.datetime.now().date()
    sess = Session()
    for item in sess.query(user_groups_n_hosts).filter(user_groups_n_hosts.end_time < str(date_now)): 
        print item.id, item.server_users.native_linux_user
    sess.close()

您每次都需要建立一个新的交易。 您可以为每个 运行 创建一个全新的会话对象,我们重用会话,但请确保您 commit(), rollback() or close() 在查询后使用它。

这个效果是由MySQL的一些默认策略造成的。

如果您使用 InnoDB 作为 MySQL 的默认存储引擎,那么它将在事务内部使用 REPEATABLE READ as its isolation level. This is the default, because in most cases it makes sense to avoid Non-repeatable Reads。 因此,如果您不结束交易,您每次都会从数据库中获得相同的结果。

来自SQLAlchemy docs:

The Session, whenever it is used to talk to the database, begins a database transaction as soon as it starts communicating. Assuming the autocommit flag is left at its recommended default of False, this transaction remains in progress until the Session is rolled back, committed, or closed. The Session will begin a new transaction if it is used again, subsequent to the previous transaction ending; from this it follows that the Session is capable of having a lifespan across many transactions, though only one at a time. We refer to these two concepts as transaction scope and session scope.

另一个选项,不推荐,是用autocommit=True创建会话,它将在每个查询语句后自动提交事务。