Python django south和django审计日志
我试图对现有的应用程序进行修改,以添加到其中(跟踪用户发起的模块更改),但遇到了重大错误。特别是action_user_id字段,它是LastUserField(存储指定要跟踪的更改的用户) 如果我从空白模型开始,我可以通过以下方式添加审核日志:Python django south和django审计日志,python,django,django-south,Python,Django,Django South,我试图对现有的应用程序进行修改,以添加到其中(跟踪用户发起的模块更改),但遇到了重大错误。特别是action_user_id字段,它是LastUserField(存储指定要跟踪的更改的用户) 如果我从空白模型开始,我可以通过以下方式添加审核日志: from audit_log.models.managers import AuditLog ... class SomeModel(models.Model) ... audit_log = AuditLog() 应用这个简单的更改
from audit_log.models.managers import AuditLog
...
class SomeModel(models.Model)
...
audit_log = AuditLog()
应用这个简单的更改并在django south执行schemamigration很容易理解,这给了我一个错误:
! Cannot freeze field 'myapp.mymodelauditlogentry.action_user'
! (this field has class audit_log.models.fields.LastUserField)
! South cannot introspect some fields; this is probably because they are custom
! fields. If they worked in 0.6 or below, this is because we have removed the
! models parser (it often broke things).
! To fix this, read http://south.aeracode.org/wiki/MyFieldsDontWork
我阅读了myfieldsdontworkwiki(以及自定义字段/内省部分),但并不100%清楚我需要做什么才能让字段正常工作
我尝试添加:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ["^audit_log\.models\.fields\.LastUserField"])
我的models.py允许./manage.py模式迁移创建迁移脚本,而前面的错误消失了。但是,当我尝试迁移(应用迁移)时,会出现以下错误:
Running migrations for myapp:
- Migrating forwards to 0004_auto__add_mymodelauditlogentry.
> my_app:0004_auto__add_mymodelauditlogentry
Traceback (most recent call last):
File "./manage.py", line 11, in <module>
execute_manager(settings)
File "/usr/local/lib/python2.6/dist-packages/Django-1.2.3-py2.6.egg/django/core/management/__init__.py", line 438, in execute_manager
utility.execute()
File "/usr/local/lib/python2.6/dist-packages/Django-1.2.3-py2.6.egg/django/core/management/__init__.py", line 379, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python2.6/dist-packages/Django-1.2.3-py2.6.egg/django/core/management/base.py", line 191, in run_from_argv
self.execute(*args, **options.__dict__)
File "/usr/local/lib/python2.6/dist-packages/Django-1.2.3-py2.6.egg/django/core/management/base.py", line 220, in execute
output = self.handle(*args, **options)
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/management/commands/migrate.py", line 105, in handle
ignore_ghosts = ignore_ghosts,
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/migration/__init__.py", line 191, in migrate_app
success = migrator.migrate_many(target, workplan, database)
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/migration/migrators.py", line 221, in migrate_many
result = migrator.__class__.migrate_many(migrator, target, migrations, database)
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/migration/migrators.py", line 292, in migrate_many
result = self.migrate(migration, database)
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/migration/migrators.py", line 125, in migrate
result = self.run(migration)
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/migration/migrators.py", line 93, in run
south.db.db.current_orm = self.orm(migration)
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/migration/migrators.py", line 246, in orm
return migration.orm()
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/utils.py", line 62, in method
value = function(self)
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/migration/base.py", line 422, in orm
return FakeORM(self.migration_class(), self.app_label())
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/orm.py", line 46, in FakeORM
_orm_cache[args] = _FakeORM(*args)
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/orm.py", line 125, in __init__
self.models[name] = self.make_model(app_label, model_name, data)
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/orm.py", line 318, in make_model
field = self.eval_in_context(code, app, extra_imports)
File "/usr/local/lib/python2.6/dist-packages/South-0.7.3-py2.6.egg/south/orm.py", line 236, in eval_in_context
return eval(code, globals(), fake_locals)
File "<string>", line 1, in <module>
File "/usr/local/lib/python2.6/dist-packages/django_audit_log-0.2.1-py2.6.egg/audit_log/models/fields.py", line 12, in __init__
super(LastUserField, self).__init__(User, null = True, **kwargs)
TypeError: __init__() got multiple values for keyword argument 'null'
除此之外,审核日志中间件不起作用,因为myapp\u mymodelauditlogentry中没有通过“id”引用“auth\u user”的action\u user\u id整数字段。然后我手动应用SQL(sqlite语法;通过在新创建的数据库上使用sqliteman获得)
它是有效的。如果有人解释我应该如何在django south的环境中通过迁移/内省做到这一点,而不必使用原始的依赖于数据库的SQL,我仍然会给予奖励
此外,我还为action_user_id创建了一个索引
CREATE INDEX "myapp_mymodelauditlogentry_26679921" ON "myapp_mymodelauditlogentry" ("action_user_id")
我发现散列26679921是基于具有“%x%”(abs(散列(('action\u user\u id'))%4294967296L,)
的字段名创建的,而不是基于任何其他内容(因此应该始终是_26679921,除非数据库要求使用长名称)。我不确定索引的名称是否重要;但我希望安全。这是最后的答案(和解释)
向南迁移时,不仅存储模型中字段的名称,还存储传递给它的类型和参数。这样做的结果是,South必须了解哪些参数由字段给出,哪些参数应该存储
因此,当您创建这样的规则时:
add_introspection_rules([], ["^audit_log\.models\.fields\.LastUserField"])
(
'action_user',
self.gf('audit_log.models.fields.LastUserField')(
related_name='_somemodel_audit_log_entry',
null=True,
to=orm['auth.User'],
)
),
rules = [(
(fields.LastUserField,),
[],
{
'to': ['rel.to', {'default': User}],
'null': ['null', {'default': True}],
},
)]
add_introspection_rules(
rules,
['^audit_log\.models\.fields\.LastUserField'],
)
Than South将创建一个具有如下列的表:
add_introspection_rules([], ["^audit_log\.models\.fields\.LastUserField"])
(
'action_user',
self.gf('audit_log.models.fields.LastUserField')(
related_name='_somemodel_audit_log_entry',
null=True,
to=orm['auth.User'],
)
),
rules = [(
(fields.LastUserField,),
[],
{
'to': ['rel.to', {'default': User}],
'null': ['null', {'default': True}],
},
)]
add_introspection_rules(
rules,
['^audit_log\.models\.fields\.LastUserField'],
)
您可以看到,它有一个相关的\u name
参数、一个null
参数和一个to
参数。现在让我们来看一下字段定义:
class LastUserField(models.ForeignKey):
"""
A field that keeps the last user that saved an instance
of a model. None will be the value for AnonymousUser.
"""
def __init__(self, **kwargs):
models.ForeignKey.__init__(self, User, null=True, **kwargs)
#print kwargs
#super(LastUserField, self).__init__(User, null = True, **kwargs)
def contribute_to_class(self, cls, name):
super(LastUserField, self).contribute_to_class(cls, name)
registry = registration.FieldRegistry(self.__class__)
registry.add_field(cls, self)
我们在这里看到了什么?ForeignKey
的第一个参数是user(第一个参数是to
属性)。第二个参数(也是硬编码的)是null
参数。结果是,当应用迁移时,South
和您的字段将尝试设置这些参数
您会得到错误:
TypeError: __init__() got multiple values for keyword argument 'null'
我们如何解决这个问题
好的,我们可以告诉南方,我们将这些参数作为默认值传递,这样它就可以安全地忽略它们
因此,我们创建了一组如下规则:
add_introspection_rules([], ["^audit_log\.models\.fields\.LastUserField"])
(
'action_user',
self.gf('audit_log.models.fields.LastUserField')(
related_name='_somemodel_audit_log_entry',
null=True,
to=orm['auth.User'],
)
),
rules = [(
(fields.LastUserField,),
[],
{
'to': ['rel.to', {'default': User}],
'null': ['null', {'default': True}],
},
)]
add_introspection_rules(
rules,
['^audit_log\.models\.fields\.LastUserField'],
)
因此,South现在了解了如何存储参数以及哪些参数需要忽略。因此,新字段定义如下:
(
'action_user',
self.gf('audit_log.models.fields.LastUserField')(
related_name='_somemodel_audit_log_entry'
)
),
正如我们所看到的,
相关的\u名称
仍然在这里,但是to
和null
参数已经消失。因此,现在我们可以安全地应用迁移而不会产生冲突。尽管使用了@WoLpH答案中的步骤,但我仍然无法创建迁移。我不得不修改audit_log/models/fields.py文件。以下是我的LastUserField字段的外观:
class LastUserField(models.ForeignKey):
"""
A field that keeps the last user that saved an instance
of a model. None will be the value for AnonymousUser.
"""
def __init__(self, **kwargs):
kwargs.pop('null', None)
kwargs.pop('to', None)
super(LastUserField, self).__init__(User, null = True, **kwargs)
def contribute_to_class(self, cls, name):
super(LastUserField, self).contribute_to_class(cls, name)
registry = registration.FieldRegistry(self.__class__)
registry.add_field(cls, self)
在我不得不这样做之前,以下内容已添加到我的models.py文件(该文件不起作用):
rules = [((fields.LastUserField,),
[],
{
'to': ['rel.to', {'default': User}],
'null': ['null', {'default': True}],
},)]
# Add the rules for the `LastUserField`
add_introspection_rules(rules, ['^audit_log\.models\.fields\.LastUserField'])
有什么建议可以避免这种黑客行为吗?问题是南方不知道如何迁移你的田地。对于自定义字段,您需要添加自己的自省规则,在这些规则中,您可以告诉south哪些参数是重要的,哪些可以忽略。如果今晚有时间的话,我会写一个如何让内省正常工作的例子。@WoLpH:是的,让内省工作起来是个问题,但谢天谢地,有内省的文档。我是模式迁移/django-south(以及django审计日志)的新手,对此我并不清楚。我尝试的前几件事与LastUserField不兼容。如果您能让内省工作起来,我将不胜感激(因此我不需要手动注入SQL),但说实话,我停止了自己的尝试,转而关注其他问题。我一直很忙,所以还没有时间给您一个正确的答案。请再容忍我一会儿:)(或者其他人可以解释一下)。长话短说,它不起作用,因为你没有告诉南方有关参数,所以它忽略了它,这导致它被传递了两次。我是django审计日志的作者。项目的GitHub页面位于此处:。如果人们在那里报告这样的问题和补丁,这将使软件包受益。我还没有将该应用程序与south结合使用,但我会关注这个问题。我已经创建并发布了关于这一点,并会考虑解决这个问题的最佳方法。