38

是否可以在 SQLAlchemy 中创建列,该列将在上次插入/更新时自动填充时间?

我创建了模型,继承自基类

class Base(object):
    def __tablename__(self):
        return self.__name__.lower()
    id = Column(Integer, primary_key=True)
    last_time = Column(TIMESTAMP, server_default=func.now())

Base = declarative_base(cls=Base)

class EntityModel(Base):
    __tablename__ = 'entities'
    settlement_id = Column(Integer, ForeignKey('settlements.id'), nullable=False)
    type = Column(String(20), nullable=False)
    level = Column(Integer, nullable=False, default=0)
    energy = Column(Float, nullable=False, default=0)
    position_x = Column(Integer, default=0)
    position_y = Column(Integer, default=0)

    def __repr__(self):
        return "<Entity('%s')>" % (self.type)

每次更新 EntityModel 时,我都希望last_timesystem function.now(). 我可以使用触发器在数据库级别执行此操作,但如果可能的话,我宁愿在应用程序级别执行此操作。

4

3 回答 3

64

在 Base 类中,在最后一条语句中添加 onupdate,如下所示:

from sqlalchemy.sql import func

last_time = Column(TIMESTAMP, server_default=func.now(), onupdate=func.current_timestamp())
于 2012-12-20T19:02:54.020 回答
14

如果你使用 MySQL,我相信你只能有一个自动更新的 datetime 列,所以我们使用 SQLAlchemy 的事件触发器来代替。

您只需将侦听器附加到“before_insert”和“before_update”挂钩并根据需要进行更新:

from sqlalchemy import event

@event.listen(YourModel, 'before_insert')
def update_created_modified_on_create_listener(mapper, connection, target):
  """ Event listener that runs before a record is updated, and sets the create/modified field accordingly."""
  target.created = datetime.utcnow()
  target.modified = datetime.utcnow()

@event.listen(YourModel, 'before_update')
def update_modified_on_update_listener(mapper, connection, target):
  """ Event listener that runs before a record is updated, and sets the modified field accordingly."""
  # it's okay if this field doesn't exist - SQLAlchemy will silently ignore it.
  target.modified = datetime.utcnow()

我知道没有人会记得将它添加到新模型中,所以我试图变得聪明并为他们添加它。

我们所有的模型都继承自一个我们巧妙地称为“DatabaseModel”的基础对象。我们检查谁继承了这个对象,并动态地将触发器添加到所有这些对象中。

如果模型没有 created 或 modified 字段也没关系 - SQLAlchemy 似乎默默地忽略它。

class DatabaseModel(db.Model):
  __abstract__ = True

  #...other stuff...

  @classmethod
  def _all_subclasses(cls):
    """ Get all subclasses of cls, descending. So, if A is a subclass of B is a subclass of cls, this
    will include A and B.
    (Does not include cls) """
    children = cls.__subclasses__()
    result = []
    while children:
      next = children.pop()
      subclasses = next.__subclasses__()
      result.append(next)
      for subclass in subclasses:
        children.append(subclass)
    return result

def update_created_modified_on_create_listener(mapper, connection, target):
  """ Event listener that runs before a record is updated, and sets the create/modified field accordingly."""
  # it's okay if one of these fields doesn't exist - SQLAlchemy will silently ignore it.
  target.created = datetime.utcnow()
  target.modified = datetime.utcnow()

def update_modified_on_update_listener(mapper, connection, target):
  """ Event listener that runs before a record is updated, and sets the modified field accordingly."""
  # it's okay if this field doesn't exist - SQLAlchemy will silently ignore it.
  target.modified = datetime.utcnow()


for cls in DatabaseModel._all_subclasses():
  event.listen(cls, 'before_insert',  update_created_modified_on_create_listener)
  event.listen(cls, 'before_update',  update_modified_on_update_listener)
于 2012-12-20T19:43:02.343 回答
3

毫无价值的是,如果您遵循 Rachel Sanders 的建议,您绝对应该这样做:

if object_session(target).is_modified(target, include_collections=False):
    target.modified = datetime.utcnow()

作为 update_modified_on_update_listener() 事件侦听器的一部分,否则您将执行大量冗余数据库更新。查看“ before_update ”部分下的http://docs.sqlalchemy.org/en/latest/orm/events.html#mapper-events以获取更多信息。

于 2015-10-12T09:58:44.510 回答