この記事は Django Advent Calendar 2017 の7日目の記事です。
Djangoのモデルクラスを継承した場合、Metaに特に指定が無ければ、マルチテーブル継承となり、migrateを実行すると2つのテーブルができます。
モデルを継承して単一のテーブルにする方法は2つあります。
- 親のモデルクラスでMetaに
abstract = True
を指定する
- 継承した子クラスでMetaに
proxy = True
を指定する
今回は後者のproxyを指定する方法を紹介します。
https://docs.djangoproject.com/en/2.0/topics/db/models/#proxy-models
この方法は、継承元の親クラスがサードパーティライブラリ等から提供されて、直接書き換えられない場合などに便利です。
継承してproxyを指定したモデル
Djangoに組込みの django.contrib.auth.models.User
を継承してみます。
from django.contrib.auth.models import User
class MyUser(User):
"""
Userモデルを継承するが、新たにテーブルは作られない。
"""
class Meta:
proxy = True
def display(self):
"""追加で定義したメソッド
"""
print("{full_name}さん".format(full_name=self.get_full_name()))
呼び出し例
>>> from myapp.models import MyUser
>>> MyUser(first_name="Shinya", last_name="Okano").display()
Shinya Okanoさん
ハマりどころ
ForeignKeyなどで参照した場合には、継承した子クラスではなく、ForeignKeyで指定された元のモデルのインスタンスが返されます。
たとえば上記の例だと、 django.contrib.auth.models.Group
などから辿った場合は、Userクラスのインスタンスになります。
>>> user = Group.objects.first().user_set.filter(first_name="Shinya").first()
>>> user
<User: >
>>> user.display() # User.displayはない
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'User' object has no attribute 'display'
回避するには、proxyのモデルを起点にQuerySetを作るか、インスタンスの __class__
を無理やり書き換えるなどですが、スマートなやり方が現状ないですね。
>>> user.display()
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'User' object has no attribute 'display'
>>> user.__class__ = MyUser
>>> user
<MyUser: >
>>> user.display()
Shinya Okanoさん
まあ、やっていきましょう。