読者です 読者をやめる 読者になる 読者になる

Djangoでモデルクラスを差し替え可能にする

Python Django

Django1.5からdjango.contrib.authのユーザーモデル(User)を差し替えることが可能になっているが、この機能を実現しているのがMetaサブクラスのswappableオプション。
このオプションを使うと、settingsの設定でモデルクラスを差し替える機能をアプリケーションで提供できる。
差し替えを行うと、差し替え前のモデルはsyncdbの対象からも外れる。
試したバージョンはDjango1.6、Python3.3。

注意点

swappableオプションはプライベートAPIなので、今後Djangoのバージョンアップで予告なく変更、削除される可能性がある。
ドキュメントにはこの機能について意図的に書いてないみたい。
#19103 (Docs for swappable model option) – Django
アプリケーションコードでこの機能を使うことは推奨しない。

サンプルコード

ベースとなるアプリケーション(baseapp)では、抽象モデルであるAbstractItemクラスを用意し、その実装をItemクラスとする。
Itemクラスは差し替え可能として、別のアプリケーション(customapp)でCustomItemモデルを作成してそれに差し替えてみる。

baseapp/models.py

Metaのswappableには、settingsファイルの変数名を指定する。この例では、settings.BASEAPP_ITEM_MODELで差し替えるモデルクラスを指定する。
また、直接モデルクラスを使用できないので、モデルクラスを取得するためのget_item_model関数を作成している。

from django.db import models


class AbstractItem(models.Model):
    """抽象モデル
    """
    name = models.CharField(max_length=100)

    class Meta:
        abstract = True


class Item(AbstractItem):
    """syncdb対象となるモデル
    差し替え可能とする
    """
    class Meta(AbstractItem.Meta):
        swappable = 'BASEAPP_ITEM_MODEL'
        db_table = 'item'

    def display(self):
        return self.name


def get_item_model():
    """Itemモデルクラスを取得する関数
    """
    from django.conf import settings
    app_label, model_name = settings.BASEAPP_ITEM_MODEL.split('.')
    item_model = models.get_model(app_label, model_name)
    return item_model


def print_all_items():
    """Itemモデルのデータを取得して画面に表示する関数
    """
    item_model = get_item_model()
    for item in item_model.objects.all():
        print(item.display())
customapp/models.py
from django.db import models
from baseapp.models import AbstractItem


class CustomItem(AbstractItem):
    """カスタマイズしたモデル
    """
    price = models.IntegerField()

    class Meta(AbstractItem.Meta):
        db_table = 'custom_item'

    def display(self):
        return "{}: {}".format(self.name, self.price)
swappable_model_proj/settings.py(抜粋)

差し替えるモデルクラスの指定は、 app_label.Model の形式となる。

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'baseapp',
    'customapp',
)   

BASEAPP_ITEM_MODEL = 'customapp.CustomItem'
customapp/admin.py

管理画面へ登録する際はCustomItemを登録する。baseapp/admin.pyでItemモデルを登録するように書かれていても、そちらは登録されない。

from django.contrib import admin

from .models import CustomItem

admin.site.register(CustomItem)

実行結果

管理画面からCustomItemのデータを追加して、shellからprint_all_items関数を実行した結果。

>>> from baseapp import models
>>> models.print_all_items()
spam: 200
ham: 150
egg: 100
>>> models.get_item_model()
<class 'customapp.models.CustomItem'>

参考

ドキュメントは無いので、django.contrib.authのソースコードを参考にした。