.. _ref-contrib-contenttypes: ============================ contenttypes フレームワーク ============================ :revision-up-to: 11321 (1.1) unfinished .. module:: django.contrib.contenttypes :synopsis: インストール済のモデルを扱うための汎用インタフェースを提供します。 Django には、 :mod:`contenttypes` アプリケーションが付属しています。 このアプリケーションを使うと、 Django で作成したプロジェクト中に存在する全 てのモデルを追跡できます。また、 ``contenttypes`` では、モデルを扱うための 高水準かつ汎用的なインタフェースを提供しています。 概要 ===== ``contenttypes`` アプリケーションの心臓部は、 :mod:`django.contrib.contenttypes.models` で定義されている :class:`~django.contrib.contenttypes.models.ContentType` モデルです。 :class:`django.contrib.contenttypes.models.ContentType` のインスタンスは、 プロジェクト上にインストールされているモデルの情報を表現したり保存したりし ています。 :class:`~django.contrib.contenttypes.models.ContentType` のイン スタンスは、新たにモデルをインストールすると自動的に生成されます。 :class:`~django.contrib.contenttypes.models.ContentType` のインスタンスは、 インスタンスの表現するモデルクラスを返したり、そのモデルクラスに対してオブ ジェクトをクエリするためのメソッドを提供しています。また、 :class:`~django.contrib.contenttypes.models.ContentType` モデルは :class:`~django.contrib.contenttypes.models.ContentType` を扱ったり、特定 のモデルの :class:`~django.contrib.contenttypes.models.ContentType` を取り 出すための :ref:`カスタムマネジャ ` を持っています。 あるモデルから :class:`~django.contrib.contenttypes.models.ContentType` に リレーションを張れば、そのモデルからインストール済みの任意のモデルのインス タンスとの間に、「一般化リレーション(generic relation)」を張れます。 ``contenttypes`` フレームワークのインストール ================================================ ``contenttypes`` フレームワークは、 ``django-admin.py startproject`` の生成するデフォルトの ``settings.py`` の :setting:`INSTALLED_APPS` リスト に入っています。リストから除外してしまっている場合や、 :setting:`INSTALLED_APPS` を自分で設定した場合には、 :setting:`INSTALLED_APPS` に ``'django.contrib.contenttypes'`` を追加してフ レームワークをインストールしてください。 通常は、 ``contenttypes`` フレームワークをインストールしておいた方がよいで しょう。 Django にバンドルされている以下のアプリケーションには、 ``contenttypes`` フレームワークが必要だからです: * ``admin`` アプリケーションは、管理インタフェース上で追加変更したオブ ジェクトの履歴を管理するために ``contenttypes`` を使います。 * Django の :mod:`認証フレームワーク ` は、ユーザ パーミッションをモデルに結びつけるために ``contenttypes`` を使います。 * Django のコメントシステム (:mod:`django.contrib.comments`) は、インス トールされている任意のモデルに対してコメントを付けられる機能を ``contenttypes`` で実現しています。 ``ContentType`` モデル ======================== .. class:: models.ContentType :class:`~django.contrib.contenttypes.models.ContentType` のインスタン スには 3 つのフィールドがあり、 3 つを組み合わせると、インストールされ ているモデルを一意に表現できます: .. attribute:: models.ContentType.app_label モデルの入っているアプリケーションの名前です。この名前はモデルの :attr:`app_label` 属性から取り出され、通常は Python の import パス の *末尾* の部分が入っています。例えば、アプリケーションが ``django.contrib.contenttypes`` なら、 :attr:`app_label` は ``contenttypes`` です。 .. attribute:: models.ContentType.model モデルクラスの名前です。 .. attribute:: models.ContentType.name 人間可読なモデル名です。この値は、モデルの :attr:`verbose_name ` から取り出されます。 例を挙げて、 ``contenttypes`` の仕組みを見てみましょう。 ``contenttypes`` アプリケーションをインストールしておき、 :setting:`INSTALLED_APPS` 設定に :mod:`sites アプリケーション ` を追加してから、 ``manage.py syncdb`` を実行してみてください。 ``django.contrib.sites.models.Site`` モデルがデータベースにインストールされ るはずです。それに伴って、以下の値がセットされた :class:`~django.contrib.contenttypes.models.ContentType` の新たなインスタ ンスが生成されているはずです: * :attr:`app_label` は ``'sites'`` (``django.contrib.sites`` の末尾) に設定 されています。 * :attr:`model` は ``'site'`` に設定されています。 * :attr:`name` は ``'site'`` に設定されています。 .. _the verbose_name attribute: ../model-api/#verbose-name .. _`verbose_name 属性`: `the verbose_name attribute`_ ``ContentType`` インスタンスのメソッド ======================================= .. class:: models.ContentType :class:`~django.contrib.contenttypes.models.ContentType` インスタンス には、インスタンスの表現しているモデルクラスを取得したり、モデルクラス のインスタンスを取り出したりするためのメソッドがあります: .. method:: models.ContentType.get_object_for_this_type(**kwargs) :class:`~django.contrib.contenttypes.models.ContentType` インスタンス の表現しているモデルでサポートされている `照合メソッドの引数 ` を取り、モデルインスタンスの `get() 照合 ` を行って、対応するオブジェクトを返します。 .. method:: models.ContentType.model_class() :class:`~django.contrib.contenttypes.models.ContentType` インスタンス の表現しているモデルクラスを返します。 例を挙げましょう。まず、 :class:`~django.contrib.auth.models.User` モデル の :class:`~django.contrib.contenttypes.models.ContentType` インスタンスを 照合します:: >>> from django.contrib.contenttypes.models import ContentType >>> user_type = ContentType.objects.get(app_label="auth", model="user") >>> user_type 取り出した ``User`` の :class:`~django.contrib.contenttypes.models.ContentType` から、 ``User`` モデルクラスを取り出したり、 ``User`` インスタンスを照合したりできます:: >>> user_type.model_class() >>> user_type.get_object_for_this_type(username='Guido') :meth:`~django.contrib.contenttypes.models.ContentType.get_object_for_this_type` と :meth:`~django.contrib.contenttypes.models.ContentType.model_class` を組 み合わせると、非常に重要なユースケースを実現できます: 1. 二つのメソッドを使えば、特定のモデルクラスを import して使うのではな く、インストールされている全てのモデルに対して操作を行えるような一般 化された高水準のコードを書けます。実行時に :attr:`app_label` と ``model`` を指定して :class:`~django.contrib.contenttypes.models.ContentType` を照合し、 得られたモデルクラスからオブジェクトを取り出せるのです。 2. 別のモデルから :class:`~django.contrib.contenttypes.models.ContentType` にリレーショ ンを張って、モデルのインスタンスと、 :class:`~django.contrib.contenttypes.models.ContentType` の表すモデ ルクラスを結び付け、モデルクラスのメソッドにアクセスできます。 Django にバンドルされているアプリケーションには、後者のテクニックを使ってい るものがいくつかあります。例えば、 Django の認証フレームワークに入っている :class:`パーミッションのシステム ` では、 :class:`~django.contrib.auth.models.Permission` モデルの中で、 :class:`~django.contrib.contenttypes.models.ContentType` への外部キー を張っています。そうすることで、 :class:`~django.contrib.auth.models.Permission` は「ブログにエントリを追加」 や「ニュースストーリーを削除」といった権限を表現できます。 ``ContentTypeManager`` カスタムマネジャ ----------------------------------------- .. class:: models.ContentTypeManager :class:`~django.contrib.contenttypes.models.ContentType` には :class:`~django.contrib.contenttypes.models.ContentTypeManager`` という カスタムマネジャがあります。このカスタムマネジャには、以下のメソッドが あります: .. method:: models.ContentTypeManager.clear_cache() ロード済みの :class:`~django.contrib.contenttypes.models.ContentType` インスタ ンスを保持しておくための内部キャッシュをクリアします。このメソッド を自分で呼ぶことはほとんどないでしょう。 Django は必要に応じて自動 的にメソッドを呼び出します。 .. method:: models.ContentTypeManager.get_for_model(model) モデルクラスやモデルのインスタンスを引数にとり、そのモデルを表す :class:`~django.contrib.contenttypes.models.ContentType` インスタ ンスを返します。 :class:`~django.contrib.contenttypes.models.ContentType` を扱いたいけれど も、わざわざモデルのメタデータを取り出して :class:`~django.contrib.contenttypes.models.ContentType` を手動で照合する のが面倒な場合には :meth:`~models.ContentTypeManager.get_for_model()` メソッ ドが特に便利です:: >>> from django.contrib.auth.models import User >>> user_type = ContentType.objects.get_for_model(User) >>> user_type .. _generic-relations: 一般化リレーション =================== 上で述べた :class:`~django.contrib.auth.models.Permission` モデルの例のよう に、自作のモデルから :class:`~django.contrib.contenttypes.models.ContentType` に外部キーを張れ ば、モデルを別のモデルクラスに結び付けられます。しかし、もう一歩踏み込めば、 :class:`~django.contrib.contenttypes.models.ContentType` を使って真の一般 化リレーション (または多態性: polymorphic リレーション) を実現できます。 簡単な例として、以下のようなタグシステムを挙げましょう:: from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic class TaggedItem(models.Model): tag = models.SlugField() content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') def __unicode__(self): return self.tag 通常の :class:`~django.db.models.fields.related.ForeignKey` は、他のモデル を「指し示す」だけに過ぎません。従って、 ``TaggedItem`` モデルで :class:`~django.db.models.fields.related.ForeignKey` を使う場合、ただ一つの モデルに対してしかタグを保存できません。この問題を解決し、任意のモデルにリ レーションを張れるようにするために、 ``contenttypes`` アプリケーションでは、 特殊なフィールドタイプ、 :class:`django.contrib.contenttypes.generic.GenericForeignKey` を提供してい ます。 :class:`~django.contrib.contenttypes.generic.GenericForeignKey` のセッ トアップは、以下の 3 つのパートに分かれています: 1. :class:`~django.contrib.contenttypes.models.ContentType` への :class:`~django.db.models.fields.related.ForeignKey` を定義します。 2. リレーション先のモデルインスタンスの主キー値を保存するためのモデル フィールドを定義します (ほとんどのモデルでは、 :class:`~django.db.models.fields.IntegerField` または :class:`~django.db.models.fields.PositiveIntegerField` です)。 このフィールドは、 GeneicRelation の対象モデルの主キーと同じ型でな ければなりません。例えば、モデルフィールドに :class:`~django.db.models.fields.IntegerField` を使っ ているなら、対象モデルの主キーに :class:`~django.db.models.fields.CharField` を使うことはできません。 3. :class:`~django.contrib.contenttypes.generic.GenericForeignKey` を定 義します。 :class:`~django.contrib.contenttypes.generic.GenericForeignKey` の引 数に、上で定義したフィールドの名前を指定します。フィールド名が :class:`~django.contrib.contenttypes.generic.GenericForeignKey` の探 すデフォルトの名前である ``content_type`` や ``object_id`` の場合に は、引数を省略してかまいません。 これで、通常の :class:`~django.db.models.fields.related.ForeignKey` に似た API を実現できます。 ``TaggedItem`` はリレーションを張っている対象のモデル インスタンスを返す ``content_object`` フィールドを持つようになります。 ``content_object`` の値は、 ``TaggedItem`` を生成するときに指定できます:: >>> from django.contrib.auth.models import User >>> guido = User.objects.get(username='Guido') >>> t = TaggedItem(content_object=guido, tag='bdfl') >>> t.save() >>> t.content_object Due to the way :class:`~django.contrib.contenttypes.generic.GenericForeignKey` is implemented, you cannot use such fields directly with filters (``filter()`` and ``exclude()``, for example) via the database API. They aren't normal field objects. These examples will *not* work:: # This will fail >>> TaggedItem.objects.filter(content_object=guido) # This will also fail >>> TaggedItem.objects.get(content_object=guido) 逆方向の一般化リレーション ---------------------------- リレーション対象にどのモデルがよく使われるのかが分かっていれば、 「逆方向の」一般化リレーションを張って、 API を追加できます。例を挙げましょ う:: class Bookmark(models.Model): url = models.URLField() tags = generic.GenericRelation(TaggedItem) これで、 ``Bookmark`` インスタンスは ``tags`` 属性をそなえます。この属性を 使うと、インスタンスにリレーションを張っている ``TaggedItems`` を取り出せま す:: >>> b = Bookmark(url='http://www.djangoproject.com/') >>> b.save() >>> t1 = TaggedItem(content_object=b, tag='django') >>> t1.save() >>> t2 = TaggedItem(content_object=b, tag='python') >>> t2.save() >>> b.tags.all() [, ] Just as :class:`django.contrib.contenttypes.generic.GenericForeignKey` accepts the names of the content-type and object-ID fields as arguments, so too does ``GenericRelation``; if the model which has the generic foreign key is using non-default names for those fields, you must pass the names of the fields when setting up a ``GenericRelation`` to it. For example, if the ``TaggedItem`` model referred to above used fields named ``content_type_fk`` and ``object_primary_key`` to create its generic foreign key, then a ``GenericRelation`` back to it would need to be defined like so:: tags = generic.GenericRelation(TaggedItem, content_type_field='content_type_fk', object_id_field='object_primary_key') 逆方向のリレーションを張らなければ、手動で照合する必要があります:: >>> b = Bookmark.objects.get(url='http://www.djangoproject.com/') >>> bookmark_type = ContentType.objects.get_for_model(b) >>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id, ... object_id=b.id) [, ] Note that if the model with a :class:`~django.contrib.contenttypes.generic.GenericForeignKey` that you're referring to uses a non-default value for ``ct_field`` or ``fk_field`` (e.g. the :mod:`django.contrib.comments` app uses ``ct_field="object_pk"``), you'll need to pass ``content_type_field`` and ``object_id_field`` to :class:`~django.contrib.contenttypes.generic.GenericRelation`.:: comments = generic.GenericRelation(Comment, content_type_field="content_type", object_id_field="object_pk") :class:`~django.contrib.contenttypes.generic.GenericRelation`` を持つオブジェ クトを削除すると、このオブジェクトに :class:`~django.contrib.contenttypes.generic.GenericForeignKey` でリレーショ ンを張っているオブジェクトも全て削除されるので注意してください。つまり、上 の例では、 ``Bookmark`` オブジェクト ``b`` を削除すると、 ``b`` にリレーショ ンを張っている ``t1`` や ``t2`` といった ``TaggedItem`` も同時に削除される のです。 Generic relations and aggregation --------------------------------- :ref:`Django's database aggregation API ` や :ref:`admin ` のドキュメントを参照してください。 .. class:: generic.GenericInlineModelAdmin :class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin` クラスは、 :class:`~django.contrib.admin.options.InlineModelAdmin` の全てのプロパティを継承していますが、一般化リレーションを扱うために、 以下の二つの属性を備えています: .. attribute:: generic.GenericInlineModelAdmin.ct_field モデル内の :class:`~django.contrib.contenttypes.models.ContentType` の外部キーフィールド名です。デフォルトの値は ``content_type`` です。 .. attribute:: generic.GenericInlineModelAdmin.ct_fk_field リレーション先オブジェクトの ID を表す整数フィールドの名前です。 デフォルトの値は ``object_id`` です。