python - 为什么这个 SQLAlchemy 示例将更改提交到数据库?

标签 python session sqlalchemy commit

这个例子说明了我在构建的应用程序中遇到的一个谜。应用程序需要支持一个选项,允许用户在不实际向数据库提交更改的情况下运行代码。但是,当我添加此选项时,我发现即使我没有调用 commit() 方法,更改也会保存到数据库中。

我的具体问题可以在代码注释中找到。基本目标是更清楚地了解 SQLAlchemy 何时以及为何提交给数据库。

我的更广泛的问题是我的应用程序是否应该 (a) 使用全局 Session 实例,或者 (b) 使用全局 Session 类,从中可以获取特定实例实例化。基于这个例子,我开始认为正确答案是 (b)。是对的吗? 编辑:this SQLAlchemy documentation建议 (b) 是推荐的。

import sys

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'

    id   = Column(Integer, primary_key = True)
    name = Column(String)
    age  = Column(Integer)

    def __init__(self, name, age = 0):
        self.name = name
        self.age  = 0

    def __repr__(self):
        return "<User(name='{0}', age={1})>".format(self.name, self.age)

engine = create_engine('sqlite://', echo = False)
Base.metadata.create_all(engine)

Session = sessionmaker()
Session.configure(bind=engine)

global_session = Session() # A global Session instance.
commit_ages    = False     # Whether to commit in modify_ages().
use_global     = True      # If True, modify_ages() will commit, regardless
                           # of the value of commit_ages. Why?

def get_session():
    return global_session if use_global else Session()

def add_users(names):
    s = get_session()
    s.add_all(User(nm) for nm in names)
    s.commit()

def list_users():
    s = get_session()
    for u in s.query(User): print ' ', u

def modify_ages():
    s = get_session()
    n = 0
    for u in s.query(User):
        n += 10
        u.age = n
    if commit_ages: s.commit()

add_users(('A', 'B', 'C'))
print '\nBefore:'
list_users()
modify_ages()
print '\nAfter:'
list_users()

最佳答案

tl;dr - 更新并未实际提交到数据库——它们是正在进行的未提交事务的一部分。


我对您对 create_engine() 的调用做了 2 处单独的更改。 (除了这一行,我完全按照发布的方式使用您的代码。)

第一个是

engine = create_engine('sqlite://', echo = True)

这提供了一些有用的信息。我不打算在这里发布完整的输出,但请注意,在 第二次调用 list_users() 之前,不会发出任何 SQL 更新命令:

...
After:
xxxx-xx-xx xx:xx:xx,xxx INFO sqlalchemy.engine.base.Engine.0x...d3d0 UPDATE users SET age=? WHERE users.id = ?
xxxx-xx-xx xx:xx:xx,xxx INFO sqlalchemy.engine.base.Engine.0x...d3d0 (10, 1)
...

这是一个线索,表明数据没有持久化,而是保存在 session 对象中。

我做的第二个改变是将数据库持久化到一个文件中

engine = create_engine('sqlite:///db.sqlite', echo = True)

再次运行脚本会提供与第二次调用 list_users() 之前相同的输出:

<User(name='A', age=10)>
<User(name='B', age=20)>
<User(name='C', age=30)>

但是,如果您现在打开我们刚刚创建的数据库并查询它的内容,您可以看到添加的用户已保存到数据库中,但年龄修改没有:

$ sqlite3 db.sqlite "select * from users"
1|A|0
2|B|0
3|C|0

因此,对 list_users() 的第二次调用是从 session 对象而不是数据库中获取其值,因为正在进行的事务尚未提交。为了证明这一点,请将以下行添加到脚本的末尾:

s = get_session()
s.rollback()
print '\nAfter rollback:'
list_users()

关于python - 为什么这个 SQLAlchemy 示例将更改提交到数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3998546/

相关文章:

python - 在python中序列化 BeautifulSoup 和xpath树

php - 设置窗口位置为同一页面 PHP 购物车

java - Vaadin 7 UI session 过早关闭

python - 除运行 py 文件外,无法在 python-eve 中包含模型

Python脚本从终端输入指定数量的元素

python - 在 VSCode 中运行当前 Python 单元测试的快捷方式

python - 将一个正在运行的程序的输出流通过管道传输到另一正在运行的程序的输入流

jsp - 增加 servlet session cookie

python - 如何在异步 sqlalchemy 中正确处理多对多?

python - 通过sqlite在sql alchemy中远程创建数据库