Django の Storage よくわかんないけど無理やり握りつぶして粉砕した話(から、できるだけ例外処理しないロジックをつくりましょうの話)

Django の Storage ってのがあんまりよくわかっていない

とりあえず公式ドキュメントは以下
カスタムのストレージシステムを作成する — Django v1.0 documentation
基本的にはなんかこう OS のファイルシステムみたいな感じにさわれる?まあストレージっていうくらいだからどこかに保存して、 open してなにかこうファイルをよしなにするためのシステムって感じなのは触っていてわかった感じ。Storage には open とか listdir とか copy とかファイルシステムを扱う上での便利メソッドが生えている。

空文字を copy できないっぽい

具体的には擬似コード的にはこんな感じ

copy_settings = []

for hoge in HogeModel.objects.all():
    # これ、 append のほうがいいきがするけどどうなんだろう……
    copy_settings += get_path()

# コピー
for src, dst in copy_settings:
    try:
        # Storage のインスタンス(だとおもう)
        src_storage.copy(
            src,
            dst,
            # これも Storage のインスタンス(だとおもう)
            to_storage=dst_storage,
        )
    except IOError:
        pass

こんな感じであるストレージからあるストレージへコピーするという処理をしていた。ただそのときいろいろ処理をしていた関係で空文字列 u'' が紛れ込んでしまった。空文字だと IOError で落ちるのだ。なのでとりえあず潰した。ああ、綺麗じゃない。行儀が良くない。というかいいロジックな気がしない。例外を raise なり return して吐かないのはすごい美しくない気がするけど、今回はそうもいってられなくて無理やり潰してしまった。

オチ

そんなものはありません。
ストレージまわりむずかしいなぁってのもあるけど、そもそもその内部ロジック(この場合 get_path())が美しくない。もっといい感じにしたかったけど、なんかうまくいかなくて、とりあえず無理やり実装した。はぁ。いいのかなぁ。

ってかいたんだけど!!!!

別にその get_path()のロジック自体である状態の時はスキップするようにしたから別に try except で潰さなくてよくなりました。まる。擬似コード的残すとこんな感じ。

# なんか for でまわすんよ
for hoge in HogeModel.objects.all():
    # なんかそのインスタンスの Path をいれるんよ
    path = hoge.path
    # その path を元にしてパスの名前をつくるんよ
    path_name = generate_path_name(path)
    # このとき コピー対象にならない path_name を None で返してしまえばループから抜け出せるんよ
    if not path_name:
        continue
    ...

ね、簡単でしょう(ニッコリ)

Django の South で migrate したときに south.exceptions.GhostMigrations

migrate がなんかうまくいかない

コマンドは普通

python manage.py migrate --setting=settings...

例外がでた。同じ症状のが StackOverFlow にあったのではる
API Only - Stack Exchange

Traceback (most recent call last):
  File "manage.py", line 14, in <module>
    execute_manager(settings)
  File "/home/username/webapps/myproject/lib/python2.6/django/core/management/__init__.py", line 438, in execute_manager
    utility.execute()
  File "/home/username/webapps/myproject/lib/python2.6/django/core/management/__init__.py", line 379, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/username/webapps/myproject/lib/python2.6/django/core/management/base.py", line 191, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/home/username/webapps/myproject/lib/python2.6/django/core/management/base.py", line 220, in execute
    output = self.handle(*args, **options)
  File "/home/username/lib/python2.6/South-0.7.3-py2.6.egg/south/management/commands/migrate.py", line 105, in handle
    ignore_ghosts = ignore_ghosts,
  File "/home/username/lib/python2.6/South-0.7.3-py2.6.egg/south/migration/__init__.py", line 171, in migrate_app
    applied = check_migration_histories(applied, delete_ghosts, ignore_ghosts)
  File "/home/username/lib/python2.6/South-0.7.3-py2.6.egg/south/migration/__init__.py", line 88, in check_migration_histories
    raise exceptions.GhostMigrations(ghosts)
south.exceptions.GhostMigrations:

 ! These migrations are in the database but not on disk:
    <bodyguard: 0002_auto__add_field_asset_is_reserved__add_field_asset_is_created__add_fie>
    <bodyguard: 0003_auto__del_field_asset_is_reserved__add_field_asset_is_assigned>
 ! I'm not trusting myself; either fix this yourself by fiddling
 ! with the south_migrationhistory table, or pass --delete-ghost-migrations
 ! to South to have it delete ALL of these records (this may not be good).

これってなにかというと、要は MySQL とかに south_migrationhistory ってテーブルがあって、それを migration のたびにレコードを更新しているんだけどその database にはあって disk = migration file がないよって話。なんらかのかたちでこのエラーに遭遇したら、とりあえず警告にあるとおりに --delete-ghost-migrations してやれば解決する

python manage.py migrate --settings=settings... --delete-ghost-migrations

South はマジ便利、だけど銀の弾丸ではない

気をつけないといろいろハマりがち