36

最近我发现 SQLAlchemy 的 Column 默认值不能像我期望的那样工作:

>>> 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命令时使用,这真的让我感到困惑。为什么会更喜欢 defaultover server_default?以及如何实现我想要的?我应该在 中指定所有默认参数__init__吗?这似乎是代码重复:要更改默认值,我必须更改两次并保持这些值相等——有什么方法可以避免这种情况吗?

4

3 回答 3

26

出于以下四个原因之一,人们会更喜欢默认而不是服务器默认:

  1. 你想运行一个 Python 函数,而不是 SQL 函数,作为默认值(或者一个需要每个 INSERT Python 状态的 SQL 表达式)。

  2. 默认值是主键列的一部分。ORM 无法在没有主键的情况下加载行,因此 server_default 在使用 ORM 时通常对 PK 列没有用处。

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

  4. 您正在处理您不能/不想更改的模式。

在这种情况下,当您希望应用程序中的“foo”为“0”而与数据库操作无关时,可以选择:

  1. 使用__init__(). 是蟒蛇!

  2. 使用事件。

这是__init__()

class TestModel(Base):
   # ...

   def __init__(self):
       self.foo = 0

这是事件(特别是init 事件):

from sqlalchemy import event

@event.listens_for(Foo, "init")
def init(target, args, kwargs):
    target.foo = 0
于 2012-12-23T17:35:45.483 回答
11

您可以使用force_instant_defaultslistener fromsqlalchemy_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
于 2018-01-04T14:40:32.533 回答
6

您可以使用该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)
于 2014-07-22T16:46:28.270 回答