contenttypesフレームワーク—Djangoドキュメント

提供:Dev Guides
< DjangoDjango/docs/3.2.x/ref/contrib/contenttypes
移動先:案内検索

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は、contenttypesapp_label になります。

model

モデルクラスの名前。

さらに、次のプロパティを使用できます。

name

コンテンツタイプの人間が読める名前。 これは、モデルの verbose_name 属性から取得されます。

これがどのように機能するかを確認するために例を見てみましょう。 contenttypes アプリケーションが既にインストールされている場合は、サイトアプリケーション:setting: `INSTALLED_APPS` 設定に追加し、manage.py migrateを実行します。これをインストールするには、モデル django.contrib.sites.models.Site がデータベースにインストールされます。 これに加えて、 ContentType の新しいインスタンスが次の値で作成されます。

  • app_label'sites'(Pythonパスdjango.contrib.sitesの最後の部分)に設定されます。
  • model'site'に設定されます。


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つの非常に重要なユースケースが可能になります。

  1. これらのメソッドを使用すると、インストールされている任意のモデルでクエリを実行する高レベルのジェネリックコードを記述できます。単一の特定のモデルクラスをインポートして使用する代わりに、app_labelmodelをに渡すことができます。 ContentType は実行時にルックアップし、モデルクラスを操作するか、モデルクラスからオブジェクトを取得します。
  2. インスタンスを特定のモデルクラスに関連付ける方法として、別のモデルを 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つの部分があります。

  1. モデルに ForeignKey から ContentType を指定します。 このフィールドの通常の名前は「content_type」です。

  2. 関連するモデルの主キー値を格納できるフィールドをモデルに指定します。 ほとんどのモデルでは、これは PositiveIntegerField を意味します。 このフィールドの通常の名前は「object_id」です。

  3. モデルに 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フィールドは元の値に設定されたままになり、GenericForeignKeyNoneを返します。

>>> 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)

同様に、 GenericForeignKeyModelForm には表示されません。

ジェネリック関係を逆にする

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>]>

add()create()、またはset()を使用して、関係を作成することもできます。

>>> t3 = TaggedItem(tag='Web development')
>>> b.tags.add(t3, bulk=False)
>>> b.tags.create(tag='Web framework')
<TaggedItem: Web framework>
>>> b.tags.all()
<QuerySet [<TaggedItem: django>, <TaggedItem: python>, <TaggedItem: Web development>, <TaggedItem: Web framework>]>
>>> b.tags.set([t1, t3])
>>> b.tags.all()
<QuerySet [<TaggedItem: django>, <TaggedItem: Web development>]>

remove()呼び出しは、指定されたモデルオブジェクトを一括削除します。

>>> b.tags.remove(t3)
>>> b.tags.all()
<QuerySet [<TaggedItem: django>]>
>>> TaggedItem.objects.all()
<QuerySet [<TaggedItem: django>]>

clear()メソッドを使用して、インスタンスの関連するすべてのオブジェクトを一括削除できます。

>>> b.tags.clear()
>>> b.tags.all()
<QuerySet []>
>>> TaggedItem.objects.all()
<QuerySet []>

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 モジュールは以下を提供します。

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, absolute_max=None, can_delete_extra=True)

modelformset_factory()を使用してGenericInlineFormSetを返します。

デフォルトのcontent_typeおよびobject_idと異なる場合は、ct_fieldおよびfk_fieldを指定する必要があります。 その他のパラメーターは、 modelformset_factory()および inlineformset_factory()に記載されているパラメーターと同様です。

for_concrete_model引数は、GenericForeignKeyfor_concrete_model 引数に対応します。

バージョン3.2で変更: absolute_maxおよびcan_delete_extra引数が追加されました。

管理者の一般的な関係

django.contrib.contenttypes.admin モジュールは、 GenericTabularInline および GenericStackedInlineGenericInlineModelAdmin のサブクラス)を提供します

これらのクラスと関数により、フォームと管理者でジェネリックリレーションを使用できます。 詳細については、モデルフォームセットおよび admin のドキュメントを参照してください。

class GenericInlineModelAdmin

GenericInlineModelAdmin クラスは、 InlineModelAdmin クラスからすべてのプロパティを継承します。 ただし、一般的な関係を操作するための独自の機能がいくつか追加されています。

ct_field

モデルの ContentType 外部キーフィールドの名前。 デフォルトはcontent_typeです。

ct_fk_field

関連オブジェクトのIDを表す整数フィールドの名前。 デフォルトはobject_idです。

class GenericTabularInline
class GenericStackedInline
GenericInlineModelAdmin のサブクラスで、それぞれスタックレイアウトと表形式レイアウトです。