在GAE中记录所有Python数据库对象的审计日志
我刚开始学习Python,想把我用PHP和MS-SQL写的一个应用程序的功能在Google Apps Engine上重新实现。
我想做的一件事是模拟我在MS-SQL中某些表的当前活动,比如说有一个插入/删除/更新的触发器,它会把当前记录(变更前的状态)复制一份到一个审计表里,并且标记上日期和时间。这样,我就可以在以后查询这个审计表,查看记录经历过的变化历史。
我在StackOverflow上找到了以下代码:
class HistoryEventFieldLevel(db.Model):
# parent, you don't have to define this
date = db.DateProperty()
model = db.StringProperty()
property = db.StringProperty() # Name of changed property
action = db.StringProperty( choices=(['insert', 'update', 'delete']) )
old = db.StringProperty() # Old value for field, empty on insert
new = db.StringProperty() # New value for field, empty on delete
不过,我不太确定这段代码怎么能应用到我新数据库中的所有对象上。
我是不是应该为每个对象创建get()和put()函数,然后在put()函数里创建这个类的一个子对象,并设置它的特定属性呢?
2 个回答
0
我对谷歌应用引擎(GAE)有点生疏,而且手头没有SDK来测试,所以这里给你一些指导,帮你理清思路。
- 创建一个叫做AuditMeta的元类,然后把它应用到你想要进行审计的模型上。
- 在创建新模型类的时候,AuditMeta应该复制这个类,并在名字后面加上"_audit",同时也要复制这个类的属性。这个过程在GAE上有点复杂,因为属性本身就是描述符。
- 给每个这样的类添加一个put方法,在这个方法里为该类创建一个审计对象并保存。这样,对于表tableA中的每一行数据,你都会在tableA_audit中有一份历史记录。
比如,一个简单的Python示例(不涉及GAE)
import new
class AuditedModel(object):
def put(self):
print "saving",self,self.date
audit = self._audit_class()
audit.date = self.date
print "saving audit",audit,audit.date
class AuditMeta(type):
def __new__(self, name, baseclasses, _dict):
# create model class, dervied from AuditedModel
klass = type.__new__(self, name, (AuditedModel,)+baseclasses, _dict)
# create a audit class, copy of klass
# we need to copy attributes properly instead of just passing like this
auditKlass = new.classobj(name+"_audit", baseclasses, _dict)
klass._audit_class = auditKlass
return klass
class MyModel(object):
__metaclass__ = AuditMeta
date = "XXX"
# create object
a = MyModel()
a.put()
输出:
saving <__main__.MyModel object at 0x957aaec> XXX
saving audit <__main__.MyModel_audit object at 0x957ab8c> XXX
可以阅读审计跟踪的代码,只有200行,看看他们是如何在Django中实现的。
1
这确实是可能的,虽然有点复杂。下面是一些可以帮助你入门的小建议:
- 仅仅重写类的
put()
方法是不够的,因为实体也可以通过调用db.put()
来存储,这样不会调用你所写类中的任何方法。 - 你可以通过修改SDK来解决这个问题,让它在调用前后执行一些钩子函数,具体可以参考我博客中的文章 这里。
- 另外,你也可以在更底层实现RPC钩子,相关内容可以在另一篇博客文章中找到 这里。
- 把审计记录存储为被修改实体的子实体是个好主意,这样可以在事务中处理,虽然这需要进一步的、更复杂的修改。
- 你不需要为每个字段都记录一条记录。实体有一种自然的序列化格式,叫做协议缓冲区(Protocol Buffers),你可以简单地把实体作为编码后的协议缓冲区存储在审计记录中。如果你是在模型层操作,可以使用 model_to_protobuf 将模型转换为协议缓冲区。
- 以上所有方法都更容易在修改后存储记录,而不是在修改前存储。不过这应该不是问题——如果你需要修改前的记录,可以直接在审计日志中回溯一条记录。