# Django 5.2.8 ドキュメント

# contenttypes フレームワーク

Django には `contenttypes` アプリケーションが含まれており、 Django を使ったプロジェクトにインストールされているすべてのモデルを追跡し、モデルを扱うための高レベルで汎用的なインターフェースを提供します。

## 概要

contenttypes アプリケーションの中心にあるのは、 `django.contrib.contenttypes.models.ContentType` にある `ContentType` モデルです。 `ContentType` のインスタンスは、プロジェクトにインストールされたモデルに関する情報を表し、格納します。新しい `ContentType` のインスタンスは、新しいモデルがインストールされるたびに自動的に作成されます。

`ContentType` のインスタンスは `ContentType` が表すモデルクラスを返すメソッドと、それらのモデルからオブジェクトを問い合わせるメソッドを持っています。また、 `ContentType` を操作するためのメソッドや、特定のモデルの `ContentType` のインスタンスを取得するためのメソッドを追加する カスタムマネージャ もあります。

あなたのモデルと `ContentType` の間のリレーションシップを利用することで、あなたのモデルのインスタンスとインストールされている任意のモデルのインスタンスとの間で「汎用的（ジェネリック）」なリレーションシップを有効にすることもできます。

## contenttypes フレームワークをインストールする

contenttypes フレームワークは、`django-admin startproject` によって作成されるデフォルトの `INSTALLED_APPS` リストに含まれていますが、もし削除したり、手動で `INSTALLED_APPS` リストを設定した場合は、 `'django.contrib.contenttypes'` を `INSTALLED_APPS` 設定に追加することで有効にできます。

通常は、contenttypes フレームワークをインストールしておくのがよいでしょう。Django の他のバンドルアプリケーションのいくつかはそれを必要とします:

- admin アプリケーションは、管理インターフェイスを通じて追加・変更された各オブジェクトの履歴を記録するためにこれを使用します。

- Django の `認証フレームワーク` は、ユーザーの権限を特定のモデルに紐づけるのに使用されます。

## `ContentType` モデル

class ContentType

  `ContentType` の各インスタンスは、2 つのフィールドを持ち、それらを合わせると、インストールされたモデルを一意に特定します:

  app_label

    モデルが属するアプリケーションの名前です。これはモデルの `app_label` 属性から取得され、アプリケーションの Python インポートパスの 最後 の部分のみが含まれます。例えば、 `django.contrib.contenttypes` の `app_label` は `contenttypes` になります。

  model

    モデルクラスの名前。

  さらに、次のプロパティが利用可能です:

  name

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

どのように動作するか、例を見てみましょう。すでに `contenttypes` アプリケーションがインストールされていて、 `sites アプリケーション` を `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(using=None, **kwargs)

  モデルが表す `ContentType` に対する有効な lookup 引数 のセットを受け取り、そのモデルで `get() ルックアップ` を実行し、対応するオブジェクトを返します。 `using` 引数を使用することで、デフォルトとは異なるデータベースを指定できます。

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` モデルクラスへのアクセスを取得するために使用します:

    >>> 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_label` と `model` を `ContentType` ルックアップに渡すことで、モデルクラスを操作したり、そこからオブジェクトを取得したりすることができます。

2. `ContentType` は、別のモデルと関連付けることができ、これによってそのインスタンスを特定のモデルクラスに紐付けることができます。これらのメソッドを使用することで、これらのモデルクラスにアクセスできます。

Django のバンドルされているアプリケーションのいくつかは、後者のテクニックを利用しています。例えば、Django の認証フレームワークの `パーミッションシステム` は、 `Permission` モデルを `ContentType` の外部キーとして使っています。これにより、 `Permission` は「ブログのエントリを追加できる」とか「ニュース記事を削除できる」といった概念を表現できます。

### `ContentTypeManager`

class ContentTypeManager

  `ContentType` にはカスタムマネージャー `ContentTypeManager` もあり、以下のメソッドが追加されています:

  clear_cache()

    `ContentType` が使用する内部キャッシュをクリアし、 `ContentType` インスタンスを作成したモデルの追跡を行います。おそらく、このメソッドを自分で呼び出す必要はほとんどありません。必要なタイミングでDjangoが自動的に呼び出します。

  get_for_id(id)

    `ContentType` を ID でルックアップします。このメソッドは `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` オブジェクトをデシリアライズ時に ナチュラルキー から参照できるようにすることです。

The `get_for_model()` method is especially useful when you know you need to work with a `ContentType` but don't want to go to the trouble of obtaining the model's metadata to perform a manual lookup:

    >>> from django.contrib.auth.models import User
    >>> ContentType.objects.get_for_model(User)
    <ContentType: user>

## ジェネリックリレーション（汎用リレーション）

`ContentType` に自分のモデルの外部キーを追加することで、上記の `Permission` モデルの例のように、モデル自身を他のモデルクラスに効果的に結びつけることができます。しかし、もう一歩進んで `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.PositiveBigIntegerField()
        content_object = GenericForeignKey("content_type", "object_id")

        def __str__(self):
            return self.tag

        class Meta:
            indexes = [
                models.Index(fields=["content_type", "object_id"]),
            ]

通常の `ForeignKey` は、ほかのモデルを単一指定することしかできません。つまり、 `TaggedItem` モデルが `ForeignKey` を使用する場合、タグを格納する単一のモデルを選択する必要があります。contenttypes アプリケーションは特別なフィールド型(`GenericForeignKey`) を提供し、これにより、リレーションシップを任意のモデルと設定できます。

class GenericForeignKey

  `GenericForeignKey` の設定には3つの部分があります:

  1. モデルに `ContentType` への `ForeignKey` を持たせます。通常、このフィールドの名前は "content_type" となります。

  2. モデルに、リレーション先モデルの主キー値を格納できるフィールドを与えます。ほとんどのモデルの場合、これは `PositiveBigIntegerField` を意味します。このフィールドの通常の名前は "object_id" です。

  3. モデルに `GenericForeignKey` を与え、上記の2つのフィールド名を渡します。これらのフィールドの名前が "content_type" と "object_id" である場合、省略できます。これらは `GenericForeignKey` が探すデフォルトのフィールド名です。

  `ForeignKey` とは異なり、データベースインデックスは `GenericForeignKey` には自動的に作成 されません ので、 `Meta.indexes` を使って独自の複数カラムインデックスを追加することをお勧めします。この動作は将来 変更されるかも知れません 。

  for_concrete_model

    `False` の場合、フィールドはプロキシモデルを参照できます。デフォルトは `True` です。これは `get_for_model()` への引数 `for_concrete_model` を反映したものです。

主キーの型の互換性

"object_id" フィールドは、リレーション先モデルの主キーフィールドと同じ型である必要はありませんが、その主キーの値は、"object_id" フィールドの `get_db_prep_value()` メソッドによって同じ型に変換可能でなければなりません。

たとえば、 `IntegerField` または `CharField` のどちらかの主キーフィールドを持つモデルに対してジェネリックリレーションを許可したい場合、モデルの "object_id" フィールドには `CharField` を使用できます。整数は `get_db_prep_value()` によって文字列に変換できるためです。

最大の柔軟性を得るには、定義された最大長がない `TextField` を使用できますが、データベースのバックエンドによっては、それにより重大なパフォーマンスペナルティが発生する可能性があります。

どのフィールドタイプが最適かについて、汎用的な解決策はありません。参照するモデルを評価し、ユースケースに最も効果的な解決策を決定してください。

`ContentType` オブジェクトへの参照をシリアライズする

ジェネリックリレーションを実装したモデルからデータをシリアライズする場合 (例えば `fixtures` を生成する場合)、関連する `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` 内には表示されません。

### 逆ジェネリックリレーション (Reverse generic relation)

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` オブジェクトも同時に削除されることを意味します。

`GenericForeignKey` は、`ForeignKey` とは異なり、 `on_delete` 引数を受け入れず、この動作をカスタマイズすることはできません。必要なら、 `GenericRelation` を使用せずに、カスケード削除を回避でき、 代替の動作は `pre_delete` シグナルを介して提供できます。

### ジェネリックリレーション (generic relation) と集計 (aggregation)

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, 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` 引数は `GenericForeignKey` の `for_concrete_model` 引数に対応します。

### admin におけるジェネリックリレーション

`django.contrib.contenttypes.admin` モジュールは、 `GenericTabularInline` と `GenericStackedInline` (`GenericInlineModelAdmin` のサブクラス) を提供します。

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

class GenericInlineModelAdmin

  `GenericInlineModelAdmin` クラスは `InlineModelAdmin` クラスの全てのプロパティを継承します。しかし、ジェネリックリレーションを扱うための独自のプロパティをいくつか追加しています:

  ct_field

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

  ct_fk_field

    リレーション先オブジェクトの ID を表す整数フィールドの名前。デフォルトは `object_id` です。

class GenericTabularInline

class GenericStackedInline

  `GenericInlineModelAdmin` のサブクラスで、それぞれスタックレイアウトと表形式のレイアウトを持っています。

### `GenericPrefetch()`

class GenericPrefetch(lookup, querysets, to_attr=None)

このルックアップは `Prefetch()` に類似しており、`GenericForeignKey` 専用に使用されるべきです。`querysets` 引数は異なる `ContentType` 向けのクエリセットのリストを受け入れます。これは、結果が均質でない `GenericForeignKey` に便利です。

    >>> from django.contrib.contenttypes.prefetch import GenericPrefetch
    >>> bookmark = Bookmark.objects.create(url="https://www.djangoproject.com/")
    >>> animal = Animal.objects.create(name="lion", weight=100)
    >>> TaggedItem.objects.create(tag="great", content_object=bookmark)
    >>> TaggedItem.objects.create(tag="awesome", content_object=animal)
    >>> prefetch = GenericPrefetch(
    ...     "content_object", [Bookmark.objects.all(), Animal.objects.only("name")]
    ... )
    >>> TaggedItem.objects.prefetch_related(prefetch).all()
    <QuerySet [<TaggedItem: Great>, <TaggedItem: Awesome>]>