sqlalchemy flush()并插入id?

Elo*_*off 101 python sqlalchemy

我想做这样的事情:

f = Foo(bar='x')
session.add(f)
session.flush()

# do additional queries using f.id before commit()
print f.id # should be not None

session.commit()
Run Code Online (Sandbox Code Playgroud)

但是当我尝试时,f.id是None.我怎样才能让它发挥作用?

-担

dpb*_*dpb 116

我刚遇到同样的问题,经过测试后我发现没有这些答案就足够了.

目前,或者从sqlalchemy .6+开始,有一个非常简单的解决方案(我不知道它是否存在于先前版本中,尽管我认为它确实存在):

session.refresh()

所以,你的代码看起来像这样:

f = Foo(bar=x)
session.add(f)
session.flush()
# At this point, the object f has been pushed to the DB, 
# and has been automatically assigned a unique primary key id

f.id
# is None

session.refresh(f)
# refresh updates given object in the session with its state in the DB
# (and can also only refresh certain attributes - search for documentation)

f.id
# is the automatically assigned primary key ID given in the database.
Run Code Online (Sandbox Code Playgroud)

这是怎么做的.

  • 这越来越接近可能对我有用但我收到以下错误的答案:InvalidRequestError:无法刷新实例'<....>'.刷新后显示实例不再存在.欣赏任何洞察力. (8认同)
  • 如果您不理解`flush()`和`commit()`之间的区别,这里有一个很好的解释:http://stackoverflow.com/a/4202016/1252290 (5认同)
  • Seams 版本 1.3.1 不需要 `session.refresh(f)` (3认同)
  • 更新,我不得不使用```sessionmaker(autoflush=True)```,该组合 w/refresh() 为我提供了行 ID。#grrr (2认同)
  • @PlaidFan而不是`flush()`使用`commit()`,然后紧接着-用`session.refresh(f)`刷新它,对我有用,我使用SQLAlchemy版本`0.6.7`。 (2认同)

zzz*_*eek 57

您的示例代码应该按原样运行.Sqlalchemy应该为其提供一个值f.id,假设它是一个自动生成的主键列.主键属性在flush()过程中立即填充,因为它们是生成的,并且不需要调用commit().所以这里的答案在于映射的细节,如果正在使用的后端有任何奇怪的怪癖(例如,SQLite不为复合主键生成整数值)和/或当你发出SQL时所说的内容打开回声.

  • 你是对的,在 shell 中快速检查显示它用一个值填充主键字段。我必须调查为什么它在实践中不起作用。 (2认同)
  • "你的示例代码应该提供一个值"听起来像你说的"你应该首先给出id的值",而不是"你所拥有的代码应该按原样运行".我很清楚,只有在三读之后,你才意味着后者而不是前者.你能澄清一下吗? (2认同)

小智 13

谢谢大家.我通过修改列映射解决了我的问题.对我来说,autoincrement=True是必需的.

起源: autoincrement=True

修改后: autoincrement=True

然后 autoincrement=True

没关系!

  • 但主键不应该可以为空 (4认同)

muo*_*uon 11

其他更旧的答案中已经提到了核心解决方案,但这使用了更新的异步 API。

使用 sqlalchemy==1.4 (2.0 风格),以下似乎有效:

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine

engine = create_async_engine(
        "postgresql+asyncpg://user:pass@localhost/db",
        echo=False,
    )


# expire_on_commit=False will prevent attributes from being expired
# after commit.
async_session = sessionmaker(
    engine, expire_on_commit=False, class_=AsyncSession,
)
# default kwarg autoflush=True


async with async_session() as session: 
    async with session.begin(): 
        f = Foo(bar='x')
        session.add(f)
        print(f.id)
        # None

        await session.flush()
        print(f.id)
        # not None
    # commits transaction, closes session
Run Code Online (Sandbox Code Playgroud)


小智 11

在过去的几个小时/几天/无论如何,我试图让上述建议发挥作用。最初,我编写了所有插入函数,如下所示:

_add = User(id, user_name, email, ...)
Run Code Online (Sandbox Code Playgroud)

其中圆括号之间的所有项目都是 None、“​​user a”、“a@example.com”、... 的变量

这是我的用户表:

_add = User(id, user_name, email, ...)
Run Code Online (Sandbox Code Playgroud)

SQLAlchemy 正确处理 _add 查询,因为它插入具有自动增量 ID 的记录。此外,正如应该的那样,没有为 id 列设置默认值。

我以各种方式尝试了上述所有选项(有/没有提交、有/没有刷新、有/没有刷新、前一个、语句之间超时,等等)。我什至多次更改了整个应用程序/数据库交互。但在所有情况下,“_add.id”要么返回 0,要么返回类似“Instance '' 已被删除,或其行不存在”的内容。

刚才我想“也许我应该通过定义指定表的列名来编写有点不同的 _add 查询”,如下所示:

_add = User(id=None, user_name=user_name, email=email, etc)
Run Code Online (Sandbox Code Playgroud)

为了强调一下,请注意:_add 查询中的id=user_name=email= 。现在,按照以下顺序执行以下语句,SQLAlchemy 确实返回插入的 ID!

session.add(_add)
print(_add.id)    <-- returns None

session.flush()   <-- does **not** insert record into database, but does increment id,
                      waiting to be committed. Flush may be omitted, because
                      session.commit() unconditionally issues session.flush()*
print(_add.id)    <-- returns incremented id

session.commit()  <-- commit is needed to actually insert record into database
print(_add.id)    <-- returns incremented id
Run Code Online (Sandbox Code Playgroud)

尽管已经提供了答案,但我不清楚 _add 查询中缺少的列名,因此我的懒惰是导致我的问题的原因。我希望这可以帮助人们避免同样的问题......

SQLAlchemy 文档


小智 6

与dpb给出的答案不同,不需要刷新.一旦刷新,你可以访问id字段,sqlalchemy会自动刷新后端自动生成的id.

我遇到了这个问题并且在经过一些调查之后想出了确切的原因,我的模型是用id作为整数字段创建的,在我的形式中,id用hiddenfield表示(因为我不想在我的表单中显示id).隐藏字段默认表示为文本.一旦我用widget = hiddenInput()将表单更改为integerfield,问题就解决了.