gpt4 book ai didi

python - 为什么 session.merge 在设置默认值和 server_default 时行包含 null 时抛出 IntegrityError

转载 作者:行者123 更新时间:2023-11-29 13:08:05 26 4
gpt4 key购买 nike

当字段设置为不可为 nullable 时,我尝试使用 session.merge(object) 更新记录,并且使用 sqlalchemy 和服务器默认设置。当它尝试更新现有记录时,会抛出一个错误,表明违反了非空约束。我能够通过从表初始化中的记录中删除空值来解决这个问题,但这看起来很奇怪。这是预料之中的,还是我不明白服务器默认值或 session.merge 是如何工作的?

Python 版本:3.6.8

sqlalchemy 版本:1.3.4

示例代码:

import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class TestTable(Base):

__tablename__ = 'test_table'

id = sa.Column(sa.Integer, primary_key=True)
field = sa.Column(sa.String, default='Unknown', server_default=sa.text("'Unknown'"), nullable=False)
field2 = sa.Column(sa.Integer)

engine = sa.create_engine('postgresql://postgres@localhost/test')

Base.metadata.create_all(bind=engine)

session = sa.orm.sessionmaker(engine)()

record = {'id': 1, 'field': None, 'field2': 3}
session.merge(TestTable(**record))

session.commit()

session.merge(TestTable(**record))

session.commit()

抛出的错误:

IntegrityError: (psycopg2.errors.NotNullViolation) null value in column "field" violates not-null constraint
DETAIL: Failing row contains (1, null, 3).

[SQL: UPDATE test_table SET field=%(field)s WHERE test_table.id = %(test_table_id)s]
[parameters: {'field': None, 'test_table_id': 1}]
(Background on this error at: http://sqlalche.me/e/gkpj)

最佳答案

在第一次合并时,该行不存在,因此创建它:

record = {'id': 1, 'field': None, 'field2': 3}
session.merge(TestTable(**record))

...发出:

INSERT INTO test_table (id, field, field2) VALUES (%(id)s, %(field)s, %(field2)s)
{'id': 1, 'field': 'Unknown', 'field2': 3}

根据 the documentationColumndefault 参数应用于 insert,这就是为什么您可以看到 None 已被替换的原因与插入语句一起发送的参数集中的 'Unknown'。文档状态:

A scalar, Python callable, or ColumnElement expression representing the default value for this column, which will be invoked upon insert if this column is otherwise not specified in the VALUES clause of the insert.

...所以在这种情况下,“未指定”似乎应该解释为“不是”,因为您显然已经“指定”了记录<中的值 字典。与此相关的是,当您在不为任何属性提供任何值的情况下实例化一个实例时,这些属性已经隐式地持有 None 的值:

print(TestTable().field is None)  # True

如果从列定义中删除 default=... kwarg,即使您向 TestTable 构造函数提供了 field=None ,删除默认值后,该参数的值根本不会发送到数据库:

INSERT INTO test_table (id, field2) VALUES (%(id)s, %(field2)s)
{'id': 1, 'field2': 3}

在这种情况下,将触发服务器端默认值。因此,在刷新插入语句之前,SQLAlchemy 会将客户端默认为指定值的列的任何 None 值交换。

在第二次合并时,该行已存在于数据库中,因此合并实例的值将填充到代表数据库中已存在的行的实例上。在合并期间,该实例的 field 列的值从 'Unknown' 更改为 None。此值更改意味着该字段的新值将作为 更新 查询的一部分发送到数据库。请注意,客户端或服务器端的默认值均不适用于更新语句。

UPDATE test_table SET field=%(field)s WHERE test_table.id = %(test_table_id)s
{'field': None, 'test_table_id': 1}

当我们尝试将 NOT NULL 列显式设置为 NULL 时,引发了 IntegrityError

关于python - 为什么 session.merge 在设置默认值和 server_default 时行包含 null 时抛出 IntegrityError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58549937/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com