この記事は 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さん
まあ、やっていきましょう。