SQLAlchemy actualiza el padre cuando hay cambios secundarios relacionados

Estoy tratando de modelar una entidad que como una o más relaciones de uno a varios, de modo que su atributo last_modified se actualice, cuando

  • un niño es añadido o eliminado
  • un niño es modificado
  • la entidad misma se modifica

He juntado el siguiente ejemplo mínimo:

 class Config(Base): __tablename__ = 'config' ID = Column('ID', Integer, primary_key=True) name = Column('name', String) last_modified = Column('last_modified', DateTime, default=now, onupdate=now) params = relationship('ConfigParam', backref='config') class ConfigParam(Base): __tablename__ = 'config_params' ID = Column('ID', Integer, primary_key=True) ConfigID = Column('ConfigID', Integer, ForeignKey('config.ID'), nullable=False) key = Column('key', String) value = Column('value', Float) @event.listens_for(Config.params, 'append') @event.listens_for(Config.params, 'remove') def receive_append_or_remove(target, value, initiator): target.last_modified = now() @event.listens_for(ConfigParam.key, 'set') @event.listens_for(ConfigParam.value, 'set') def receive_attr_change(target, value, oldvalue, initiator): if target.config: # don't act if the parent config isn't yet set # ie during __init__ target.config.last_modified = now() 

Esto parece funcionar, pero me pregunto si hay una mejor manera de hacerlo.

Específicamente, esto se vuelve muy detallado ya que mi implementación real de ConfigParam tiene más atributos y tengo múltiples relaciones de uno a muchos configuradas en la clase de Config principal.

Tome esto con un gran grano de sal, “parece” que funciona, podría explotar:

 def rel_listener(t, v, i): t.last_modified = now() def listener(t, v, o, i): if t.config: t.config.last_modified = now() from sqlalchemy import inspect for rel in inspect(Config).relationships: event.listen(rel, 'append', rel_listener) event.listen(rel, 'remove', rel_listener) for col in inspect(ConfigParam).column_attrs: event.listen(col, 'set', listener) 

El problema es que las inspecciones no hacen excepciones y las columnas como ‘ID’ y ‘ConfigID’ estarán vinculadas a los detectores de eventos.

Otra forma, quizás un poco menos tediosa, sería usar una lista de atributos para vincular eventos de una manera similar:

 for attr in ['key', 'value']: event.listen(getattr(ConfigParam, attr), 'set', listener) 

Esto le da control sobre lo que está vinculado a los eventos y lo que no lo es.