2010-12-17 15 views
5

django-audit-logを追加するために既存のアプリケーションにdjango-south migrationを実行しようとしていますが(モジュールのユーザ起動の変更を追跡するため)、重大なエラーが発生しています。具体的には、lastUserFieldであるaction_user_idフィールド(追跡中の変更を指定したユーザーを格納します)。django-south with django-audit-log

私は空白のモデルから始めていた場合、私はちょうど経由AUDIT_LOGを追加することができます。

from audit_log.models.managers import AuditLog 
... 
class SomeModel(models.Model) 
    ... 
    audit_log = AuditLog() 

この単純な変更を適用するとDjango-南にschemamigrationをやっunderstandingly私にエラーを与える:

! 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"]) 

前のエラーが消えると./manage.py schemamigrationは、移行スクリプトを作成することができ、私のmodels.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' 

EDIT(12/20正午):私はmodels.pyに行を追加する場合、私はschemamigrationを適用することができますその「ID」で参照「AUTH_USER」myapp_mymodelauditlogentryにはaction_user_id整数フィールドが存在しない場合を除き

from south.modelsinspector import add_introspection_rules, add_ignored_fields 
add_ignored_fields(["^audit_log\.models\.fields\.LastUserField"]) 

は次にAUDIT_LOGミドルウェアは動作しません。

ALTER TABLE "myapp_mymodelauditlogentry" ADD "action_user_id" integer REFERENCES "auth_user" ("id"); 

、それが動作します。(新しく作成されたデータベースにsqlitemanを使用して得られた。sqliteの構文)それから私は、手動でSQLを適用します。誰かが私がdjango-southのコンテキストでマイグレーション/イントロスペクションを使ってどうやってやらなければならないかを説明したら、恩恵を受けるでしょう。データベースに依存しないSQLを生かす必要はなく、感謝します。

また、私はaction_user_idのインデックスを作成しました。私は、インデックスへのリードを搭載したモデルの正常な作成が、私はハッシュ26679921を'%x' % (abs(hash(('action_user_id',))) % 4294967296L,)で、フィールド名に基づいて作成され、他の何に基づいていないことを追い詰め

CREATE INDEX "myapp_mymodelauditlogentry_26679921" ON "myapp_mymodelauditlogentry" ("action_user_id") 

と呼ばれることに注意してください(これは常にあるべき_26679921)。データベースで長い名前を切り捨てる必要がある場合を除きます。インデックスの名前が重要かどうかはわかりません。しかし安全であることを望んでいた。

+1

南は、あなたのフィールドを移行する方法を知らないという問題があります。カスタムフィールドの場合、重要なパラメータと無視できるパラメータを南に指定する独自のイントロスペクションルールを追加する必要があります。私が今夜時間を取れば、イントロスペクションを適切に動作させる方法の例を書いてみましょう。 – Wolph

+0

@WoLpH:はい、イントロスペクションを動作させることが問題ですが、感謝してイントロスペクションのドキュメントがありました。私にとっては、スキーマ移行/ django-south(とdjango-audit-log)の初心者は分かりませんでした。私が試した最初のいくつかのことは、LastUserFieldで作業していなかった。あなたがイントロスペクションを手に入れることができれば(私は手動でSQLを注入する必要はありません)、感謝したいと思います。しかし、正直言って私は自分自身を試みるのを止め、他の問題に移りました。 –

+1

私はかなり忙しかったので、あなたにまだ適切な答えを与える時間がなかった。もう少し長く私と​​一緒に抱きしめてください:)(または他の誰かがそれを説明することができます)。長い話を簡単に言えば、パラメータについては南に言わなかったので、それは無視されているため、2回渡すことになります。 – Wolph

答えて

3

@ WoLpHの回答の手順を使用しても、まだ移行を作成できませんでした。私はaudit_log/models/fields.pyファイルを修正しなければなりませんでした。私はこれを行うに頼らなければならなかったの前に、次の(動作しませんでした)私のmodels.pyファイルに追加された

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) 

:ここに私のLastUserFieldフィールドがどのように見えるかだ

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']) 

任意の提案は、このハッカーを避けるために私ができることは何ですか?

+0

私はdjango-audit-logの著者です。プロジェクトのGitHubページは、https://github.com/Atomidata/django-audit-logにあります。このような問題やパッチを人々が報告すると、パッケージに役立つでしょう。私は南と一緒にアプリを使用していないが、私は問題を見ていきます。私はこれについて作成して問題を出し、これを解決する最善の方法を検討します。 – Vasil

8

ここに最終的に答えがあります。

マイグレーションするときには、モデルのフィールドの名前だけでなく、渡された型と引数も格納されます。その結果、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'], 
) 
), 

あり、related_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属性です)。 2番目の引数(ハードコーディングされている)はnullパラメータです。その結果、移行を適用するとSouthとフィールドの両方がこれらのパラメータを設定しようとします。

そして、あなたがエラーを取得:

TypeError: __init__() got multiple values for keyword argument 'null' 

は、我々はこれをどのように修正すればよいですか?

さて、私たちは、これらの引数をデフォルトとして渡しているので、無視しても問題ないことをSouthに伝えることができます。

だから我々はこのような一連のルールを作成:そのための

rules = [(           
    (fields.LastUserField,),       
    [],            
    {            
     'to': ['rel.to', {'default': User}],   
     'null': ['null', {'default': True}],   
    },            
)] 
add_introspection_rules(       
    rules,           
    ['^audit_log\.models\.fields\.LastUserField'], 
)  

を、南は現在のパラメータを格納する方法を理解し、どのパラメータが無視される必要があります。だから、新しいフィールドの定義はこのようになります。私たちが見ることができるように

(
    'action_user', 
    self.gf('audit_log.models.fields.LastUserField')(
    related_name='_somemodel_audit_log_entry' 
) 
), 

related_nameはまだここにいるが、tonullパラメータが消えてしまいました。これで、競合を起こさずに移行を安全に適用できるようになりました。

+0

ありがとうございました。 –