简体   繁体   English

如何在sqlalchemy中提交之前应用列默认值

[英]How to apply Column defaults before a commit in sqlalchemy

I have a declarative-base model: 我有一个声明基础模型:

class User(Base):
    id = Column(Integer, primary_key=True)
    money = Column(Integer, default=100)

and then I run 然后我跑了

>>> u = User()
>>> u.money
None

How can I populate the defaults using sqlalchemy without writing anything to the database? 如何在向数据库写入任何内容的情况下使用sqlalchemy填充默认值?

The column default only applies to INSERT and UPDATE statements, and thus isn't being applied until you .flush() the session. 列默认仅适用于INSERT和UPDATE语句,因此直到您.flush()会话.flush()应用。

To see the same value on new instances before you flush, you need to apply the default when a new instance is being created; 要在刷新之前在新实例上看到相同的值,您需要在创建新实例时应用默认值; in the __init__ method of the User object: User对象的__init__方法中:

class User(Base):
    __tablename__ = 'users'

    def __init__(self, **kwargs):
        if 'money' not in kwargs:
             kwargs['money'] = self.__table__.c.money.default.arg
        super(User, self).__init__(**kwargs)

    id = Column(Integer, primary_key=True)
    money = Column(Integer, default=100)

If no money attribute is being set, we add one directly based on the default configured for the column. 如果没有设置money属性,我们将根据为该列配置的默认值直接添加一个。

Note that the defaults are SQL expressions , not Python values, so you may have to map those to Python objects first. 请注意,默认值是SQL表达式 ,而不是Python值,因此您可能必须首先将它们映射到Python对象。 For example, a boolean field will have a default 'false' or 'true' string value, not a False or True Python boolean object. 例如,布尔字段将具有默认的'false''true'字符串值,而不是FalseTrue Python布尔对象。

I have discovered a mechanism for populating some defaults automatically when an instance is created. 我发现了一种在创建实例时自动填充某些默认值的机制。 I'm migrating from an in-house ORM that set defaults at instance creation, so I needed to preserve this behaviour and I didn't want to touch every model definition. 我正在从内部ORM迁移,在实例创建时设置默认值,因此我需要保留此行为,我不想触及每个模型定义。

BaseModel = declarative_base()

class Base(BaseModel):
    __abstract__ = True

    def __init__(self, **kwargs):
        for attr in self.__mapper__.column_attrs:
            if attr.key in kwargs:
                continue

            # TODO: Support more than one value in columns?
            assert len(attr.columns) == 1
            col = attr.columns[0]

            if col.default and not callable(col.default.arg):
                kwargs[attr.key] = col.default.arg

        super(Base, self).__init__(**kwargs)

The major limitation is that only non-callable defaults are supported. 主要限制是仅支持不可调用的默认值。 Callable defaults and those that use a database query require an execution context that isn't available at instance creation time. 可调用默认值和使用数据库查询的默认值需要在实例创建时不可用的执行上下文。 Also, I have not discovered a case where len(ColumnProperty.columns) != 1 but that isn't supported either. 另外,我还没有发现len(ColumnProperty.columns) != 1的情况,但也不支持。

Since I need the default values not necessarily in constructor, but sometimes from other places, I came up with a function, that I added to my base Object class: 因为我需要默认值不一定在构造函数中,但有时来自其他地方,我想出了一个函数,我添加到我的基础Object类:

Base = declarative_base()
class ItemBase(Base):
    __abstract__ = True
    def _ensure_defaults(self):
        for column in self.__table__.c:
            if getattr(self, column.name) is None and column.default is not None and column.default.is_scalar:
                setattr(self, column.name, column.default.arg)

Which obviously does not work for callables. 这显然不适用于callables。 I could not figure out how to call it, and maybe it's for the better (feel like hacking the think already). 我无法弄清楚如何调用它,也许它会变得更好(感觉就像已经破解了想法)。

With this I can do something like: 有了这个,我可以这样做:

class User(ItemBase):
    # ... inheritance, columns and stuff
    def __init__(self):
        self._ensure_defaults()

I can also call the method not in __init__ , but also from other methods, in case I only need to rely on the values in special circumstances (which I ended up removing later, since it was too confusing). 我也可以调用不在__init__的方法,也可以调用其他方法,以防我在特殊情况下只需依赖值(后来我最终删除了,因为它太混乱了)。 Still in some cases this might be more flexible than the solution of @JamesEmerton. 在某些情况下,这可能比@JamesEmerton的解决方案更灵活。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM