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

dir*_*r01 28 python sqlalchemy default-value

最近我发现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
Run Code Online (Sandbox Code Playgroud)

我希望在对象实例化之后tmodel_instance.foo立即相等0,但似乎默认值仅在执行INSERT命令时使用,它确实让我感到困惑.为什么一个人更喜欢 default结束server_default?我如何实现我想要的目标?我应该指定所有默认参数__init__吗?这似乎是代码重复:更改默认值我必须将其更改两次并保持这些值相等 - 是否有某种方法可以避免这种情况?

zzz*_*eek 21

由于以下四个原因之一,人们更喜欢默认优先于服

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

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

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

  4. 您正在处理您不能/不想更改的架构.

在这种情况下,当您在应用程序中将"foo"设置为"0"而不依赖于数据库操作时,选项为:

  1. __init__().这是蟒蛇!

  2. 使用事件.

这是__init__():

class TestModel(Base):
   # ...

   def __init__(self):
       self.foo = 0
Run Code Online (Sandbox Code Playgroud)

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

from sqlalchemy import event

@event.listens_for(Foo, "init")
def init(target, args, kwargs):
    target.foo = 0
Run Code Online (Sandbox Code Playgroud)

  • 但是为什么sqlAlchemy模型以这种方式设计:在创建之后没有默认值而不是默认值?这肯定会混淆用于其他ORM库的人 (5认同)
  • "默认"通常是SQL表达式或完整的服务器端值.在INSERT发生之前无法知道,或者在创建对象时(如果它本身降低了值为真正的"INSERT defaut"),则理想情况下不能预知它.因此,只使核心级插入默认值的一个子集行为完全不同将是不一致的. (4认同)

Ped*_*eck 5

您可以使用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)
Run Code Online (Sandbox Code Playgroud)


kra*_*ski 5

您可以使用force_instant_defaultsfrom的监听器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
Run Code Online (Sandbox Code Playgroud)