要在我的基于SQLAlchemy的应用程序中提供活动日志,我有一个这样的模型:
class ActivityLog(Base):
__tablename__ = 'activitylog'
id = Column(Integer, primary_key=True)
activity_by_id = Column(Integer, ForeignKey('users.id'), nullable=False)
activity_by = relation(User, primaryjoin=activity_by_id == User.id)
activity_at = Column(DateTime, default=datetime.utcnow, nullable=False)
activity_type = Column(SmallInteger, nullable=False)
target_table = Column(Unicode(20), nullable=False)
target_id = Column(Integer, nullable=False)
target_title = Column(Unicode(255), nullable=False)
日志包含多个表的条目,因此我不能使用ForeignKey关系。日志条目如下所示:
doc = Document(name=u'mydoc', title=u'My Test Document',
created_by=user, edited_by=user)
session.add(doc)
session.flush() # See note below
log = ActivityLog(activity_by=user, activity_type=ACTIVITY_ADD,
target_table=Document.__table__.name, target_id=doc.id,
target_title=doc.title)
session.add(log)
这给我留下了三个问题:
我必须在doc
对象获得id
之前刷新会话。如果我使用了ForeignKey列和relation
映射器,我可以简单地调用ActivityLog(target=doc)
并让SQLAlchemy完成工作。有没有办法解决需要手工冲洗的问题?
target_table
参数过于冗长。我想我可以使用ActivityLog中的target
属性设置器来解决这个问题,该属性设置器会自动从给定实例中检索表名和id。
最重要的是,我不确定如何从数据库中检索模型实例。给定一个ActivityLog实例log
,调用self.session.query(log.target_table).get(log.target_id)
不起作用,因为query()
期望模型作为参数。
一种解决方法似乎是使用多态并从ActivityLog识别的基础模型派生我的所有模型。像这样:
class Entity(Base):
__tablename__ = 'entities'
id = Column(Integer, primary_key=True)
title = Column(Unicode(255), nullable=False)
edited_at = Column(DateTime, onupdate=datetime.utcnow, nullable=False)
entity_type = Column(Unicode(20), nullable=False)
__mapper_args__ = {'polymorphic_on': entity_type}
class Document(Entity):
__tablename__ = 'documents'
__mapper_args__ = {'polymorphic_identity': 'document'}
body = Column(UnicodeText, nullable=False)
class ActivityLog(Base):
__tablename__ = 'activitylog'
id = Column(Integer, primary_key=True)
...
target_id = Column(Integer, ForeignKey('entities.id'), nullable=False)
target = relation(Entity)
如果我这样做,ActivityLog(...).target
在引用Document时会给我一个Document实例,但我不确定是否需要为所有内容提供两个表的开销。我应该继续这样做吗?
答案 0 :(得分:1)
解决这个问题的一种方法是多态关联。它应解决您的所有3个问题,并使数据库外键约束起作用。请参阅SQLAlchemy源代码中的polymorphic association example。 Mike Bayer有一个旧的blogpost,可以更详细地讨论这个问题。
答案 1 :(得分:1)
绝对浏览博客帖子和蚂蚁链接的示例。我没有找到解释上的混淆,而是假设有更多关于这个主题的经验。
我可以提出的一些建议是:
ForeignKeys
:总的来说,我同意他们是一件好事,但我不确定它在你的情况下在概念上是否重要:你似乎正在使用这个ActivityLog
作为正交交叉切割问题(AOP);但是具有外键的版本会有效地使您的业务对象意识到<{1>}的。使用架构设置将ActivityLog
用于审计目的的另一个问题是,如果允许对象FK
,deletion
要求将删除此对象的所有ActivityLog条目。FK
:每当您创建/修改(/删除)对象时,您都会手动执行所有这些日志记录。使用SA,您可以使用before_commit实现Automatic logging
,这将自动为您完成工作。这样你完全可以避免写下面的部分:
SessionExtension
EDIT-1:已添加完整的示例代码
log = ActivityLog(activity_by=user, activity_type=ACTIVITY_ADD,
target_table=Document.__table__.name, target_id=doc.id,
target_title=doc.title)
session.add(log)
标志,以便从主对象的关系
将从不加载。从对方导航时加载,
并且还可以使用自定义查询从主对象加载,如图所示
在示例中也使用lazy='noload'
readonly属性。代码(可通过某些测试运行):
activitylog_readonly