django-south 与 django-audit-log
我正在尝试对现有应用程序进行 django-south 迁移 以添加 django-audit-log 到它(跟踪用户发起的模块更改),但我遇到了重大错误。具体来说,action_user_id 字段是 LastUserField(它存储指定正在跟踪的更改的用户)。
如果我从一个空白模型开始,我可以通过以下方式添加一个audit_log:
from audit_log.models.managers import AuditLog
...
class SomeModel(models.Model)
...
audit_log = AuditLog()
应用这个简单的更改并在 django-south 中进行模式迁移显然会给我一个错误:
! 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
我阅读了 MyFieldsDontWork wiki (以及自定义字段/内省部分),但并不是 100% 清楚我需要做什么才能让这些字段发挥作用。
我尝试添加:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ["^audit_log\.models\.fields\.LastUserField"])
到我的 models.py 中,这允许 ./manage.py schemamigration 创建迁移脚本,并且先前的错误消失。但是,当我尝试迁移(应用迁移)时,出现以下错误:
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'
编辑(中午 12/20):如果我将这些行添加到 models.py 中,我可以应用 schemamigration,
from south.modelsinspector import add_introspection_rules, add_ignored_fields
add_ignored_fields(["^audit_log\.models\.fields\.LastUserField"])
除非audit_log 中间件不起作用,因为 myapp_mymodelauditlogentry 中没有通过“id”引用“auth_user”的 action_user_id 整数字段。然后我手动应用 SQL(sqlite 语法;通过在新创建的数据库上使用 sqliteman 获得。)
ALTER TABLE "myapp_mymodelauditlogentry" ADD "action_user_id" integer REFERENCES "auth_user" ("id");
并且它起作用了。如果有人解释我应该如何在 django-south 的上下文中通过迁移/内省来做到这一点,而不需要使用原始数据库依赖的 SQL,我仍然会给予赏金,并表示感激。
另外,我为 action_user_id 创建了索引。我注意到模型的正常创建会导致一个名为的索引
CREATE INDEX "myapp_mymodelauditlogentry_26679921" ON "myapp_mymodelauditlogentry" ("action_user_id")
,我发现哈希值 26679921 是根据 '%x' % (abs(hash(('action_user_id',))) 的字段名称创建的% 4294967296L,)
并且不基于任何其他内容(因此应始终为 _26679921,除非数据库要求截断长名称)。我不确定索引的名称是否重要;但想要安全。
I'm trying to do a django-south migration to an existing application to add django-audit-log to it (to track user-initiated changes of a module), but am running into significant errors. Specifically with the action_user_id field that is a LastUserField (which stores the user who specified the change that is being tracked).
If I was starting from a blank model, I could just add an audit_log via:
from audit_log.models.managers import AuditLog
...
class SomeModel(models.Model)
...
audit_log = AuditLog()
Applying this simple change and doing a schemamigration in django-south understandingly gives me an error:
! 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
I read the MyFieldsDontWork wiki (and the Custom Fields/Introspection parts), but its not 100% clear what I need to do to get the fields to work.
I try adding:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ["^audit_log\.models\.fields\.LastUserField"])
to my models.py which allowed the ./manage.py schemamigration to create a migration script with the previous error goes away. However when I try to migrate (to apply the migration), I get the following errors:
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'
EDIT (12/20 noon): I can apply the schemamigration if I add the lines to models.py
from south.modelsinspector import add_introspection_rules, add_ignored_fields
add_ignored_fields(["^audit_log\.models\.fields\.LastUserField"])
except then the audit_log middleware doesn't work as there is no action_user_id integer field in myapp_mymodelauditlogentry that references "auth_user" by "id". Then I manually apply the SQL (sqlite syntax; obtained by using sqliteman on newly created database.)
ALTER TABLE "myapp_mymodelauditlogentry" ADD "action_user_id" integer REFERENCES "auth_user" ("id");
and it works. I'll still give the bounty if someone explains how I'm supposed to do this in the context of django-south with migrations/introspection, without necessitating going to raw database dependent SQL and be grateful.
Also, I created an index for action_user_id. I notice that the normal creation of models with leads to an index called
CREATE INDEX "myapp_mymodelauditlogentry_26679921" ON "myapp_mymodelauditlogentry" ("action_user_id")
I hunted down that the hash 26679921 is created based on the field name with '%x' % (abs(hash(('action_user_id',))) % 4294967296L,)
and isn't based on anything else (so should always be _26679921 unless the database requires the long name to be trunctated). I'm not sure if the names of the index ever matter; but wanted to be safe.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
最后是答案(和解释)。
向南迁移时,不仅会存储模型中的字段名称,还会存储传递给它的类型和参数。这样做的结果是 South 必须了解字段给出了哪些参数以及应该存储哪些参数。
因此,当您创建如下规则时:
Than South 将创建一个具有如下列的表:
如您所见,其中包含一个
lated_name
参数、一个null
参数和一个to
参数。现在让我们看一下字段定义:我们在这里看到了什么?
ForeignKey
的第一个参数是 user(第一个参数是to
属性)。第二个参数(也是硬编码的)是null
参数。结果,在应用迁移时,South
和您的字段都会尝试设置这些参数。然后你会得到错误:
我们如何解决这个问题?
好吧,我们可以告诉 South,我们将这些参数作为默认值传递,以便它可以安全地忽略它们。
因此,我们创建了一组这样的规则:
因此,South 现在了解如何存储参数以及哪些参数需要被忽略。所以新的字段定义将是这样的:
正如我们所看到的,
lated_name
仍然在这里,但是to
和null
参数已经消失了。所以现在我们可以安全地应用迁移而不会发生冲突。Here's finally the answer (and explanation).
When migrating South not only stores the names of the fields in your models, but also the type and the arguments that are passed to it. The result of this is that South has to understand which parameters are given by the field and which should be stored.
So when you create a rule like this:
Than South will create a table with a column like this:
Which has, as you can see, a
related_name
parameter, anull
parameter and ato
parameter. Now let's take a look at the field definition:What do we see here? The first argument to
ForeignKey
is user (the first argument is theto
attribute). The second argument (also hardcoded) is thenull
parameter. The result, when applying the migration bothSouth
and your field will try to set these parameters.And you get the error:
How do we fix this?
Well, we can tell South that we are passing these arguments as defaults so it can safely ignore them.
So we create a set of rules like this:
Because of that, South now understands how to store the parameters and which parameters need to be ignored. So the new field definition will be this:
As we can see, the
related_name
is still here, but theto
andnull
parameters have disappeared. So now we can safely apply the migration without getting conflicts.尽管使用了 @WoLpH 的答案中的步骤,我仍然无法创建迁移。我必须修改audit_log/models/fields.py 文件。我的 LastUserField 字段如下所示:
在我不得不采取此操作之前,以下内容已添加到我的 models.py 文件中(该文件不起作用):
关于我可以采取哪些措施来避免这种黑客行为的任何建议?
Despite using the steps in @WoLpH's answer, I still couldn't create the migration. I had to modify the audit_log/models/fields.py file. Here's how my LastUserField field looks like:
The following was added to my models.py file (which didn't work) before I had to resort to doing this:
Any suggestions on what I could do to avoid this hackery?