【问题标题】:How to apply Column defaults before a commit in sqlalchemy如何在 sqlalchemy 中提交之前应用列默认值
【发布时间】:2012-11-27 07:23:12
【问题描述】:

我有一个基于声明的模型:

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

然后我跑

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

如何使用 sqlalchemy 填充默认值而不向数据库写入任何内容?

【问题讨论】:

标签: python sqlalchemy


【解决方案1】:

该列默认值仅适用于 INSERT 和 UPDATE 语句,因此在您 .flush() 会话之前不会应用。

要在刷新之前在新实例上看到相同的值,您需要在创建新实例时应用默认值;在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)

如果没有设置money属性,我们直接根据为该列配置的默认值添加一个。

请注意,默认值是 SQL 表达式,而不是 Python 值,因此您可能必须先将它们映射到 Python 对象。例如,布尔字段将具有默认的 'false''true' 字符串值,而不是 FalseTrue Python 布尔对象。

【讨论】:

  • 因此,如果我想填写更复杂对象的所有默认值,我必须遍历所有列,查看它是否具有默认值并将其填充到相应的属性中。好的,我可以编码。我只是想知道:这个功能必须存在于 sqlalchemy 中,因为无论如何它在插入/更新时都需要它。我想知道,为什么它没有暴露给用户。
  • @Elrond:可能是因为默认值是针对 SQL 端的;您也可以拥有以 SQL 表示的非静态默认值。
  • 我喜欢Martijn的解决方案,看起来很完整。我过去总是这样做,这可能会少一些代码,因为您不必遍历列。 stackoverflow.com/questions/13384996/…
  • 为了让它适用于布尔字段,我必须这样做kwargs['my_flag'] = ('true' == str(self.__table__.c.my_flag.server_default.arg)),因为它将默认值视为文本值(“false”)而不是布尔值。
  • @JayK:是的,因为正如我在之前的评论中所说:默认值是针对 SQL 端的
【解决方案2】:

我发现了一种在创建实例时自动填充一些默认值的机制。我正在从一个在创建实例时设置默认值的内部 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)

主要限制是只支持不可调用的默认值。可调用默认值和使用数据库查询的默认值需要在实例创建时不可用的执行上下文。另外,我还没有发现len(ColumnProperty.columns) != 1 但也不支持的情况。

【讨论】:

    【解决方案3】:

    由于我不一定在构造函数中需要默认值,但有时来自其他地方,我想出了一个函数,我将它添加到我的基 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)
    

    这显然不适用于可调用对象。我不知道怎么称呼它,也许它是为了更好(感觉就像已经破解了想法)。

    有了这个,我可以做类似的事情:

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

    我也可以不在__init__中调用该方法,也可以从其他方法中调用,以防我只需要在特殊情况下依赖这些值(后来我最终删除了,因为它太混乱了)。在某些情况下,这可能比@JamesEmerton 的解决方案更灵活。

    【讨论】:

      猜你喜欢
      • 2012-12-09
      • 2017-07-07
      • 1970-01-01
      • 2022-11-18
      • 1970-01-01
      • 1970-01-01
      • 2016-08-03
      • 1970-01-01
      • 2011-07-06
      相关资源
      最近更新 更多