contenttypesフレームワーク
Djangoには、Djangoを利用したプロジェクトにインストールされているすべてのモデルを追跡できる contenttypes アプリケーションが含まれており、モデルを操作するための高レベルの汎用インターフェイスを提供します。
概要
contenttypesアプリケーションの中心は、django.contrib.contenttypes.models.ContentType
にある ContentType モデルです。 ContentType のインスタンスは、プロジェクトにインストールされたモデルに関する情報を表し、保存します。 ContentType の新しいインスタンスは、新しいモデルがインストールされるたびに自動的に作成されます。
ContentType のインスタンスには、それらが表すモデルクラスを返し、それらのモデルからオブジェクトをクエリするためのメソッドがあります。 ContentType には、カスタムマネージャーもあり、 ContentType を操作したり、特定のモデルの ContentType のインスタンスを取得したりするためのメソッドを追加します。
モデルと ContentType の間の関係を使用して、モデルの1つのインスタンスとインストールしたモデルのインスタンスの間の「一般的な」関係を有効にすることもできます。
contenttypesフレームワークのインストール
contenttypesフレームワークは、django-admin startproject
によって作成されたデフォルトの:setting: `INSTALLED_APPS` リストに含まれていますが、削除した場合、または:setting:を手動で設定した場合`INSTALLED_APPS` リストの場合、'django.contrib.contenttypes'
を:setting:` INSTALLED_APPS` 設定に追加することで有効にできます。
通常、contenttypesフレームワークをインストールすることをお勧めします。 Djangoの他のバンドルアプリケーションのいくつかはそれを必要とします:
- 管理アプリケーションはこれを使用して、管理インターフェイスを介して追加または変更された各オブジェクトの履歴をログに記録します。
- Djangoの認証フレームワークは、これを使用してユーザー権限を特定のモデルに関連付けます。
ContentTypeモデル
- class ContentType
ContentType の各インスタンスには、インストールされたモデルを一意に記述する2つのフィールドがあります。
- app_label
モデルが含まれているアプリケーションの名前。 これは、モデルの app_label 属性から取得され、アプリケーションのPythonインポートパスの last 部分のみが含まれます。 たとえば、
django.contrib.contenttypes
は、contenttypes
の app_label になります。
- model
モデルクラスの名前。
さらに、次のプロパティを使用できます。
- name
コンテンツタイプの人間が読める名前。 これは、モデルの verbose_name 属性から取得されます。
これがどのように機能するかを確認するために例を見てみましょう。 contenttypes アプリケーションが既にインストールされている場合は、サイトアプリケーションを:setting: `INSTALLED_APPS` 設定に追加し、manage.py migrate
を実行します。これをインストールするには、モデル django.contrib.sites.models.Site がデータベースにインストールされます。 これに加えて、 ContentType の新しいインスタンスが次の値で作成されます。
ContentTypeインスタンスのメソッド
各 ContentType インスタンスには、 ContentType インスタンスからそれが表すモデルに取得したり、そのモデルからオブジェクトを取得したりできるメソッドがあります。
- ContentType.get_object_for_this_type(**kwargs)
- ContentType が表すモデルの有効なルックアップ引数のセットを取得し、そのモデルで get()ルックアップを実行して、対応するオブジェクトを返します。
- ContentType.model_class()
- この ContentType インスタンスによって表されるモデルクラスを返します。
たとえば、 User モデルの ContentType を検索できます。
>>> from django.contrib.contenttypes.models import ContentType
>>> user_type = ContentType.objects.get(app_label='auth', model='user')
>>> user_type
<ContentType: user>
次に、それを使用して、特定のユーザーを照会するか、User
モデルクラスにアクセスします。
>>> user_type.model_class()
<class 'django.contrib.auth.models.User'>
>>> user_type.get_object_for_this_type(username='Guido')
<User: Guido>
get_object_for_this_type()と model_class()を組み合わせることで、2つの非常に重要なユースケースが可能になります。
- これらのメソッドを使用すると、インストールされている任意のモデルでクエリを実行する高レベルのジェネリックコードを記述できます。単一の特定のモデルクラスをインポートして使用する代わりに、
app_label
とmodel
をに渡すことができます。 ContentType は実行時にルックアップし、モデルクラスを操作するか、モデルクラスからオブジェクトを取得します。 - インスタンスを特定のモデルクラスに関連付ける方法として、別のモデルを ContentType に関連付け、これらのメソッドを使用してそれらのモデルクラスにアクセスできます。
Djangoのバンドルアプリケーションのいくつかは、後者の手法を利用しています。 たとえば、Djangoの認証フレームワークのパーミッションシステムは、 ContentType への外部キーを持つ Permission モデルを使用します。 これにより、許可は「ブログエントリを追加できる」や「ニュース記事を削除できる」などの概念を表すことができます。
ContentTypeManager
- class ContentTypeManager
ContentType には、次のメソッドを追加するカスタムマネージャー ContentTypeManager もあります。
- clear_cache()
ContentType が ContentType インスタンスを作成したモデルを追跡するために、 ContentType によって使用される内部キャッシュをクリアします。 このメソッドを自分で呼び出す必要はおそらくないでしょう。 Djangoは、必要に応じて自動的に呼び出します。
- get_for_id(id)
IDで ContentType を検索します。 このメソッドは get_for_model()と同じ共有キャッシュを使用するため、通常の
ContentType.objects.get(pk=id)
よりもこのメソッドを使用することをお勧めします。
- get_for_model(model, for_concrete_model=True)
モデルクラスまたはモデルのインスタンスのいずれかを取得し、そのモデルを表す ContentType インスタンスを返します。
for_concrete_model=False
を使用すると、プロキシモデルの ContentType をフェッチできます。
- get_for_models(*models, for_concrete_models=True)
可変個引数のモデルクラスを取得し、モデルクラスをそれらを表す ContentType インスタンスにマッピングするディクショナリを返します。
for_concrete_models=False
を使用すると、プロキシモデルの ContentType をフェッチできます。
- get_by_natural_key(app_label, model)
指定されたアプリケーションラベルとモデル名で一意に識別される ContentType インスタンスを返します。 このメソッドの主な目的は、逆シリアル化中に ContentType オブジェクトが自然キーを介して参照できるようにすることです。
get_for_model()メソッドは、 ContentType を操作する必要があるが、手動ルックアップを実行するためにモデルのメタデータを取得する手間をかけたくない場合に特に便利です。 :
>>> from django.contrib.auth.models import User
>>> ContentType.objects.get_for_model(User)
<ContentType: user>
一般的な関係
上記の Permission モデルの例のように、独自のモデルの1つから ContentType に外部キーを追加すると、モデルを別のモデルクラスに効果的に結び付けることができます。 ただし、さらに一歩進んで ContentType を使用して、モデル間の真に一般的な(「ポリモーフィック」と呼ばれることもある)関係を有効にすることは可能です。
たとえば、次のようなタグ付けシステムに使用できます。
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class TaggedItem(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
def __str__(self):
return self.tag
通常の ForeignKey は、他の1つのモデルのみを「指す」ことができます。つまり、TaggedItem
モデルが ForeignKey を使用した場合、1つだけのモデルを選択する必要があります。タグを保存します。 contenttypesアプリケーションは、これを回避し、任意のモデルとの関係を可能にする特別なフィールドタイプ(GenericForeignKey
)を提供します。
- class GenericForeignKey
GenericForeignKey の設定には、次の3つの部分があります。
モデルに ForeignKey から ContentType を指定します。 このフィールドの通常の名前は「content_type」です。
関連するモデルの主キー値を格納できるフィールドをモデルに指定します。 ほとんどのモデルでは、これは PositiveIntegerField を意味します。 このフィールドの通常の名前は「object_id」です。
モデルに GenericForeignKey を指定し、上記の2つのフィールドの名前を渡します。 これらのフィールドの名前が「content_type」および「object_id」の場合は、これを省略できます。これらは、 GenericForeignKey が検索するデフォルトのフィールド名です。
- for_concrete_model
False
の場合、フィールドはプロキシモデルを参照できます。 デフォルトはTrue
です。 これは、for_concrete_model
引数を get_for_model()に反映します。
主キータイプの互換性
「object_id」フィールドは、関連するモデルの主キーフィールドと同じタイプである必要はありませんが、それらの主キー値は、 get_db_prep_value()によって「object_id」フィールドと同じタイプに強制可能である必要があります。 メソッド。
たとえば、 IntegerField または CharField の主キーフィールドを持つモデルへのジェネリックリレーションを許可する場合は、の「object_id」フィールドに CharField を使用できます。 get_db_prep_value()によって整数を文字列に強制変換できるため、モデル。
最大限の柔軟性を得るには、最大長が定義されていない TextField を使用できますが、データベースのバックエンドによっては、パフォーマンスが大幅に低下する可能性があります。
フィールドタイプが最適な万能のソリューションはありません。 指し示すと予想されるモデルを評価し、ユースケースに最も効果的なソリューションを決定する必要があります。
ContentType
オブジェクトへの参照のシリアル化
ジェネリックリレーションを実装するモデルからデータをシリアル化する場合(たとえば、フィクスチャを生成する場合)、自然キーを使用して、関連する ContentType オブジェクトを一意に識別する必要があります。 詳細については、自然キーおよびdumpdata --natural-foreign
を参照してください。
これにより、通常の ForeignKey に使用されるものと同様のAPIが有効になります。 各TaggedItem
には、関連するオブジェクトを返す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
<User: Guido>
関連するオブジェクトが削除された場合、content_type
およびobject_id
フィールドは元の値に設定されたままになり、GenericForeignKey
はNone
を返します。
>>> guido.delete()
>>> t.content_object # returns None
GenericForeignKey の実装方法により、データベースAPIを介してこのようなフィールドをフィルター(filter()
やexclude()
など)で直接使用することはできません。 GenericForeignKey は通常のフィールドオブジェクトではないため、これらの例は動作しません。
# This will fail
>>> TaggedItem.objects.filter(content_object=guido)
# This will also fail
>>> TaggedItem.objects.get(content_object=guido)
同様に、 GenericForeignKey は ModelForm には表示されません。
ジェネリック関係を逆にする
- class GenericRelation
- ;; related_query_name
- このオブジェクトに戻る関連オブジェクトの関係は、デフォルトでは存在しません。
related_query_name
を設定すると、関連オブジェクトからこのオブジェクトへのリレーションが作成されます。 これにより、関連オブジェクトからのクエリとフィルタリングが可能になります。
最も頻繁に使用するモデルがわかっている場合は、「逆」のジェネリック関係を追加して、追加のAPIを有効にすることもできます。 例えば:
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
class Bookmark(models.Model):
url = models.URLField()
tags = GenericRelation(TaggedItem)
Bookmark
インスタンスにはそれぞれtags
属性があり、関連するTaggedItems
を取得するために使用できます。
>>> b = Bookmark(url='https://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()
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
related_query_name
を設定して GenericRelation を定義すると、関連するオブジェクトからクエリを実行できます。
tags = GenericRelation(TaggedItem, related_query_name='bookmark')
これにより、TaggedItem
からBookmark
に対するフィルタリング、順序付け、およびその他のクエリ操作が可能になります。
>>> # Get all tags belonging to bookmarks containing `django` in the url
>>> TaggedItem.objects.filter(bookmark__url__contains='django')
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
もちろん、related_query_name
を追加しない場合は、同じタイプのルックアップを手動で実行できます。
>>> bookmarks = Bookmark.objects.filter(url__contains='django')
>>> bookmark_type = ContentType.objects.get_for_model(Bookmark)
>>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id, object_id__in=bookmarks)
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
GenericForeignKey がcontent-typeフィールドとobject-IDフィールドの名前を引数として受け入れるのと同様に、 GenericRelation も同様です。 汎用外部キーを持つモデルがこれらのフィールドにデフォルト以外の名前を使用している場合は、 GenericRelation を設定するときにフィールドの名前を渡す必要があります。 たとえば、上記のTaggedItem
モデルが、content_type_fk
およびobject_primary_key
という名前のフィールドを使用して汎用外部キーを作成した場合、 GenericRelation がそれに戻ります。次のように定義する必要があります。
tags = GenericRelation(
TaggedItem,
content_type_field='content_type_fk',
object_id_field='object_primary_key',
)
また、 GenericRelation を持つオブジェクトを削除すると、 GenericForeignKey がそれを指しているオブジェクトもすべて削除されることに注意してください。 上記の例では、これは、Bookmark
オブジェクトが削除された場合、それを指すすべてのTaggedItem
オブジェクトが同時に削除されることを意味します。
ForeignKey とは異なり、 GenericForeignKey はこの動作をカスタマイズするための on_delete 引数を受け入れません。 必要に応じて、 GenericRelation を使用しないことでカスケード削除を回避でき、 pre_delete シグナルを介して代替動作を提供できます。
一般的な関係と集約
Djangoのデータベース集約API は、 GenericRelation で動作します。 たとえば、すべてのブックマークに含まれるタグの数を確認できます。
>>> Bookmark.objects.aggregate(Count('tags'))
{'tags__count': 3}
フォームの一般的な関係
django.contrib.contenttypes.forms モジュールは以下を提供します。
BaseGenericInlineFormSet
- GenericForeignKey で使用するためのフォームセットファクトリ generic_inlineformset_factory()。
- class BaseGenericInlineFormSet
- generic_inlineformset_factory(model, form=ModelForm, formset=BaseGenericInlineFormSet, ct_field='content_type', fk_field='object_id', fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, validate_max=False, for_concrete_model=True, min_num=None, validate_min=False)
modelformset_factory()を使用して
GenericInlineFormSet
を返します。デフォルトの
content_type
およびobject_id
と異なる場合は、ct_field
およびfk_field
を指定する必要があります。 その他のパラメーターは、 modelformset_factory()および inlineformset_factory()に記載されているパラメーターと同様です。for_concrete_model
引数は、GenericForeignKey
の for_concrete_model 引数に対応します。
管理者の一般的な関係
django.contrib.contenttypes.admin モジュールは、 GenericTabularInline および GenericStackedInline ( GenericInlineModelAdmin のサブクラス)を提供します
これらのクラスと関数により、フォームと管理者でジェネリックリレーションを使用できます。 詳細については、モデルフォームセットおよび admin のドキュメントを参照してください。
- class GenericInlineModelAdmin
GenericInlineModelAdmin クラスは、 InlineModelAdmin クラスからすべてのプロパティを継承します。 ただし、一般的な関係を操作するための独自の機能がいくつか追加されています。
- ct_field
モデルの ContentType 外部キーフィールドの名前。 デフォルトは
content_type
です。
- ct_fk_field
関連オブジェクトのIDを表す整数フィールドの名前。 デフォルトは
object_id
です。
- class GenericTabularInline
- class GenericStackedInline
- GenericInlineModelAdmin のサブクラスで、それぞれスタックレイアウトと表形式レイアウトです。