Djangoのアプリケーションでmodelsモジュールを複数ファイルに分割する

Djangoのアプリケーションで、models.pyのコード量が多くなってきたので、複数のファイルに分割する話。
ハマりどころがあるので書いておく。試したのはPython2.7、Django1.4。

testapp/
  __init__.py
  models.py
models.py
from django.db import models

class Foo(models.Model):
    value = models.IntegerField()

class Bar(models.Model):
    value = models.IntegerField()

modelsをディレクトリにする

まず、models.pyと同じディレクトリ階層にmodelsディレクトリを作成し、__init__.pyとmodels.pyを分割したものを置く

testapp/
  __init__.py
  models/
    __init__.py
    foo.py
    bar.py
foo.py
from django.db import models

class Foo(models.Model):
    value = models.IntegerField()
bar.py
from django.db import models

class Bar(models.Model):
    value = models.IntegerField()

これだけでは、syncdbコマンドなどでモデルが認識されない。

__init__.pyの編集

syncdbコマンドなどでは、{アプリケーション名}.modelsのような名前のモジュールからModelクラスを継承したクラスを探している。つまり models/__init__.py で分割した各モジュールのモデルをインポートするようにすればよい。

__init__.py
from testapp.models.foo import Foo
from testapp.models.bar import Bar

さて、これで問題ないかと思いきや、もう一点変更しないとsyncdbで認識されない。

Meta.app_labelの変更

モデルが所属するアプリケーションの名前をapp_labelで設定する必要がある。app_labelを設定しない場合は、モデルが定義されたモジュールの1つ上の階層のモジュール名になる。
syncdbなどの際に、アプリケーション名とapp_labelが一致しないものは認識されない。

foo.py
from django.db import models

class Foo(models.Model):
    value = models.IntegerField()

    class Meta:
        app_label = 'testapp'
bar.py
from django.db import models

class Bar(models.Model):
    value = models.IntegerField()

    class Meta:
        app_label = 'testapp'

以上でsyncdbなどで認識される状態でmodelsを複数ファイルに分割できる。