web-dev-qa-db-ja.com

前回挿入/更新されたときに自動的に時間が入力されるSQLAlchemyで列を作成することは可能ですか?

前回挿入/更新されたときに自動的に時刻が入力される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を更新するたびに、system function.now()で_last_time_を更新したいと思います。これは、トリガーを使用してデータベースレベルで実行できますが、可能であれば、アプリケーションレベルで実行したいと思います。

28
PaolaJ.

基本クラスで、次のように最後のステートメントにonupdateを追加します。

last_time = Column(TIMESTAMP, server_default=func.now(), onupdate=func.current_timestamp())
42
tigeronk2

MySQLを使用している場合、自動更新の日時列は1つしかないと思います。そのため、代わりに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」と呼ばれるベースオブジェクトから継承しています。このオブジェクトから誰が継承するかを確認し、それらすべてにトリガーを動的に追加します。

モデルに作成または変更されたフィールドがなくても問題ありません-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)
12
Rachel Sanders

レイチェル・サンダースの推奨に従うなら、絶対にすべきことは何の価値もありません。

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

update_modified_on_update_listener()イベントリスナーの一部として、それ以外の場合は、大量の冗長データベース更新を実行します。チェックアウト http://docs.sqlalchemy.org/en/latest/orm/events.html#mapper-events 詳細については、「before_update」セクションの下にあります。

3
advance512