为什么在提交对象之前SQLAlchemy默认列值不可用?

2024-06-08 23:10:38 发布

您现在位置:Python中文网/ 问答频道 /正文

最近我发现SQLAlchemy的列默认值并不像我所期望的那样工作:

>>> Base = declarative_base()
>>> class TestModel(Base):
    ...     __tablename__ = 'tmodel'
...     id = sa.Column(sa.Integer, primary_key=True)
...     foo = sa.Column(sa.Integer, default=0)
...    
>>> tmodel_instance = TestModel()
>>> print tmodel_instance.foo
None
>>> session.add(tmodel_instance)
>>> print tmodel_instance.foo
None
>>> session.commit()
>>> print tmodel_instance.foo
0

我希望在对象实例化之后tmodel_instance.foo等于0,但似乎默认值只在执行INSERT命令时使用,这让我很困惑。为什么人们更喜欢default而不是server_default?我怎样才能达到我想要的?我应该在__init__中指定所有默认参数吗?这似乎是代码重复:要更改默认值,我必须更改两次并保持这些值相等——有什么方法可以避免这种情况?


Tags: instancenonedefaultbasefoosqlalchemysessionsa
3条回答

与服务器默认值相比,人们更喜欢默认值,原因有四个:

  1. 您希望在默认情况下运行一个Python函数,而不是SQL函数(或者一个还需要每次插入Python状态的SQL表达式)。

  2. 默认值是主键列的一部分。ORM不能在没有主键的情况下加载行,因此当使用ORM时,服务器默认值通常对PK列不起作用。

  3. 数据库不支持将要运行的SQL表达式作为“服务器默认值”。

  4. 您正在处理一个无法/不想更改的架构。

在本例中,当您希望应用程序中的“foo”独立于数据库操作为“0”时,可以选择:

  1. 使用__init__()。是Python!

  2. 使用事件。

这里是__init__()

class TestModel(Base):
   # ...

   def __init__(self):
       self.foo = 0

以下是事件(特别是init event):

from sqlalchemy import event

@event.listens_for(Foo, "init")
def init(target, args, kwargs):
    target.foo = 0

您可以使用init事件来填充默认值。此事件侦听器将执行以下操作:

from sqlalchemy import event
from sqlalchemy.orm import mapper
from sqlalchemy.inspection import inspect


def instant_defaults_listener(target, args, kwargs):
    for key, column in inspect(target.__class__).columns.items():
        if column.default is not None:
            if callable(column.default.arg):
                setattr(target, key, column.default.arg(target))
            else:
                setattr(target, key, column.default.arg)


event.listen(mapper, 'init', instant_defaults_listener)

可以使用force_instant_defaults中的sqlalchemy_utils侦听器更改此行为:

from sqlalchemy_utils import force_instant_defaults

force_instant_defaults()

class TestModel(Base):
    __tablename__ = 'tmodel'
    id = sa.Column(sa.Integer, primary_key=True)
    foo = sa.Column(sa.Integer, default=0)

model = TestModel()
assert model.foo == 0

相关问题 更多 >