Ответ 1
В базовом классе добавьте onupdate в последнем утверждении следующим образом:
last_time = Column(TIMESTAMP, server_default=func.now(), onupdate=func.current_timestamp())
Можно ли создать столбец в 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_time
обновлялся на system function.now()
. Я могу сделать это на уровне базы данных с помощью триггеров, но я бы предпочел сделать это на уровне приложения, если это возможно.
В базовом классе добавьте onupdate в последнем утверждении следующим образом:
last_time = Column(TIMESTAMP, server_default=func.now(), onupdate=func.current_timestamp())
Если вы используете MySQL, я считаю, что у вас может быть только один номер обновления для даты и времени, поэтому мы используем триггеры 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 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 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 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)
Не стоит ничего, если вы будете следовать рекомендации Рейчел Сандерс, вам обязательно нужно:
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" для получения дополнительной информации.