# Documentation Django 5.2.8

# L’infrastructure des types de contenus (contenttypes)

Django inclut une application `contenttypes` qui permet de recenser l’ensemble des modèles installés dans un projet Django, fournissant une interface générique de haut niveau pour travailler avec les modèles.

## Aperçu

Le cœur de l’application contenttypes est le modèle `ContentType`, définir dans `django.contrib.contenttypes.models.ContentType`. Les instances de `ContentType` représentent et stockent des informations sur les modèles installés dans un projet ; de nouvelles instances de `ContentType` sont automatiquement créées à chaque fois que de nouveaux modèles sont installés.

Les instances de `ContentType` ont des méthodes pour renvoyer les classes de modèle qu’elles représentent et pour interroger les objets de ces modèles. `ContentType` dispose également d’un gestionnaire personnalisé qui ajoute des méthodes pour travailler avec `ContentType` et pour obtenir des instances de `ContentType` pour un modèle particulier.

Les relations entre vos modèles et `ContentType` peuvent également être utilisées pour établir des relations « génériques » entre une instance d’un de vos modèles et des instances de n’importe quel autre modèle que vous avez installé.

## Installation du système contenttypes

Le système contenttypes est inclus par défaut dans la liste `INSTALLED_APPS` créée par `django-admin startproject`, mais si vous l’avez supprimé ou si vous définissez manuellement votre liste `INSTALLED_APPS`, vous pouvez l’activer en ajoutant `'django.contrib.contenttypes'` à votre réglage `INSTALLED_APPS`.

C’est généralement une bonne idée d’avoir l’application contenttypes installée ; plusieurs autres applications fournies avec Django ont en besoin :

- L’application d’administration l’utilise pour tracer l’historique de chaque objet ajouté ou modifié via l’interface d’administration.

- Le `système d'authentification` de Django l’utilise pour associer les permissions des utilisateurs à des modèles spécifiques.

## Le modèle `ContentType`

class ContentType

  Chaque instance de `ContentType` a deux champs qui, pris ensemble, décrivent de manière unique un modèle installé :

  app_label

    Le nom de l’application dont le modèle fait partie. Il provient de l’attribut `app_label` du modèle et ne comprend que la dernière partie du chemin d’importation Python de l’application ; par exemple, l’attribut `app_label` de `django.contrib.contenttypes` devient `contenttypes`.

  model

    Le nom de classe du modèle.

  De plus, la propriété suivante est disponible :

  name

    Le nom lisible du type de contenu. Il provient de l’attribut `verbose_name` du modèle.

Examinons un exemple pour voir comment cela fonctionne. Si l’application `contenttypes` est déjà installée, que vous ajoutez ensuite l’application `sites` à votre réglage `INSTALLED_APPS` et que vous exécutez `manage.py migrate` pour l’installer, le modèle `django.contrib.sites.models.Site` sera installé dans votre base de données. Dans le même temps, une nouvelle instance de `ContentType` sera créée avec les valeurs suivantes :

- `app_label` sera défini à `'sites'` (la dernière partie du chemin Python de `django.contrib.sites`).

- `model` sera défini à `'site'`.

## Méthodes des instances `ContentType`

Chaque instance `ContentType` possède des méthodes qui permettent de passer d’une instance `ContentType` au modèle qu’elle représente, ou de récupérer des objets à partir de ce modèle :

ContentType.get_object_for_this_type(using=None, **kwargs)

  Accepte un ensemble de paramètres de recherche valides pour le modèle que `ContentType` représente, et réalise `une recherche get()` sur ce modèle, renvoyant l’objet correspondant. L’argument `using` peut être utilisé pour indiquer une base de données différente de celle par défaut.

ContentType.model_class()

  Renvoie la classe du modèle représenté par cette instance `ContentType`.

Par exemple, nous pourrions chercher le `ContentType` du modèle `User`:

    >>> from django.contrib.contenttypes.models import ContentType
    >>> user_type = ContentType.objects.get(app_label="auth", model="user")
    >>> user_type
    <ContentType: user>

Puis l’utiliser pour rechercher un `User` particulier ou pour accéder à la classe du modèle `User`:

    >>> user_type.model_class()
    <class 'django.contrib.auth.models.User'>
    >>> user_type.get_object_for_this_type(username="Guido")
    <User: Guido>

Ensemble, `get_object_for_this_type()` et `model_class()` permettent deux cas d’utilisation extrêmement importants :

1. En utilisant ces méthodes, vous pouvez écrire du code générique de haut niveau qui effectue des requêtes sur n’importe quel modèle installé ; au lieu d’importer et d’utiliser une unique classe de modèle spécifique, vous pouvez passer des variables `app_label` et `model` pour chercher un objet `ContentType` au moment de l’exécution, et travailler ensuite avec la classe de modèle ou récupérer des objets avec.

2. Vous pouvez associer un autre modèle à un objet `ContentType` afin de lier ses instances à certaines classes de modèle, et utiliser ces méthodes pour obtenir l’accès à ces classes de modèle.

Plusieurs des applications intégrées à Django font usage de cette dernière technique. Par exemple, le `système de permissions` dans le système d’authentification de Django utilise un modèle `Permission` avec une clé étrangère vers `ContentType`; cela permet à `Permission` de représenter des concepts tels que « peut ajouter une entrée de blog » ou « peut supprimer une actualité ».

### Le gestionnaire `ContentTypeManager`

class ContentTypeManager

  `ContentType` possède aussi un gestionnaire personnalisé, `ContentTypeManager`, qui ajoute les méthodes suivantes :

  clear_cache()

    Efface un cache interne utilisé par `ContentType` pour garder une trace des modèles pour lesquels il a créé des instances `ContentType`. Vous n’aurez probablement jamais besoin d’appeler cette méthode de vous-même ; Django l’appelle automatiquement lorsque c’est nécessaire.

  get_for_id(id)

    Recherche un `ContentType` par son identifiant. Comme cette méthode utilise le même cache partagé que `get_for_model()`, il est préférable d’utiliser cette méthode à l’habituel `ContentType.objects.get (pk=id)`.

  get_for_model(model, for_concrete_model=True)

    Accepte soit une classe de modèle, soit une instance de modèle, et renvoie l’instance `ContentType` représentant ce modèle. `for_concrete_model=False` permet de récupérer l’instance `ContentType` d’un modèle mandataire.

  get_for_models(*models, for_concrete_models=True)

    Accepte un nombre variable de classes de modèle et renvoie un dictionnaire associant les classes de modèle aux instances `ContentType` qui les représentent. `for_concrete_model=False` permet de récupérer les instances `ContentType` de modèles mandataires.

  get_by_natural_key(app_label, model)

    Renvoie l’instance `ContentType` identifiée de façon unique par l’étiquette d’application et le nom du modèle donnés. L’objectif principal de cette méthode est de permettre aux objets `ContentType` d’être référencés par une clé naturelle pendant la désérialisation.

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>

## Relations génériques

L’ajout d’une clé étrangère depuis l’un de vos propres modèles vers un `ContentType` permet au modèle de se lier efficacement à une autre classe de modèle, comme dans l’exemple du modèle `Permission` ci-dessus. Mais il est possible d’aller encore plus loin et d’utiliser `ContentType` pour permettre de véritables relations génériques (parfois appelées « polymorphes ») entre les modèles.

Par exemple, on pourrait l’utiliser pour un système d’étiquetage comme ceci

    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"]),
            ]

Une clé `ForeignKey` normale ne peut « pointer » que vers un seul autre modèle, ce qui signifie que si le modèle `TaggedItem` utilisait une clé `ForeignKey`, il devrait choisir un et un seul modèle pour stocker les étiquettes associées. L’application contenttypes fournit un type de champ spécial (`GenericForeignKey`) qui résout cette problématique et permet que la relation se fasse avec n’importe quel modèle :

class GenericForeignKey

  Il y a trois étapes à suivre pour mettre en place une `GenericForeignKey`:

  1. Ajoutez à votre modèle une clé `ForeignKey` vers `ContentType`. Le nom usuel de ce champ est « content_type ».

  2. Ajoutez à votre modèle un champ qui peut stocker des valeurs de clé primaire provenant des modèles qui feront l’objet du lien. Pour la plupart des modèles, cela correspond à un champ `PositiveBigIntegerField`. Le nom usuel de ce champ est « object_id ».

  3. Ajoutez à votre modèle une clé `GenericForeignKey` en lui passant les noms des deux champs décrits ci-dessus. Si ces champs sont nommés « content_type » et « object_id », vous pouvez les omettre, car ce sont les noms de champs par défaut que `GenericForeignKey` va chercher.

  Au contraire du champ `ForeignKey`, il n’y a pas de création automatique d’index de base de données pour un champ `GenericForeignKey`, il est donc recommandé d’utiliser `Meta.indexes` pour ajouter vos propres index multicolonnes. Ce comportement pourrait changer dans le futur.

  for_concrete_model

    Si cet attribut vaut `False`, le champ sera en mesure de référencer des modèles mandataires. La valeur par défaut est `True`. Cela fait écho au paramètre `for_concrete_model` de `get_for_model()`.

Compatibilité de type de clé primaire

Le champ `object_id` n’a pas besoin d’être du même type que les champs de clé primaire des modèles connexes, mais leurs valeurs de clé primaire doivent pouvoir être transformées dans le même type que le champ `object_id` par leur méthode `get_db_prep_value()`.

Par exemple, si vous souhaitez autoriser les relations génériques de modèles avec des champs de clé primaire `IntegerField` ou `CharField`, vous pouvez utiliser `CharField` comme type de champ `object_id` du modèle puisque les nombres entiers peuvent être forcés vers des chaînes de caractères par `get_db_prep_value()`.

Pour une flexibilité maximale, vous pouvez utiliser un champ `TextField` qui n’a pas de longueur maximale définie, mais cela pourrait entraîner des pénalités de performances significatives en fonction du moteur de base de données.

Il n’y a pas de solution passe-partout concernant le meilleur type de champ. Vous devez évaluer les modèles que vous prévoyez de lier et déterminer quelle solution sera la plus efficace pour votre cas d’utilisation.

Sérialisation des références aux objets `ContentType`

Si vous sérialisez des données (par exemple, lors de la génération de `fixtures`) à partir d’un modèle qui met en œuvre les relations génériques, vous devriez probablement utiliser une clé naturelle pour identifier de manière unique les objets `ContentType` associés. Voir clés naturelles et `dumpdata --natural-foreign` pour plus d’informations.

Cela activera une API similaire à celle utilisée pour une clé `ForeignKey` normale ; chaque `TaggedItem` aura un champ `content_object` qui renvoie l’objet auquel il est lié, et vous pouvez également attribuer une valeur à ce champ ou l’utiliser lors de la création d’un `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>

Si l’objet lié est supprimé, les champs `content_type` et `object_id` conservent leur valeur originale et le champ `GenericForeignKey` renvoie `None`:

    >>> guido.delete()
    >>> t.content_object  # returns None

En raison de la façon dont `GenericForeignKey` est implémentée, vous ne pouvez pas utiliser ces champs directement avec les filtres (`filter()` et `exclude()`, par exemple) par l’intermédiaire de l’API de base de données. Comme une clé `GenericForeignKey` n’est pas un objet de champ normal, ces exemples ne fonctionneront pas :

    # This will fail
    >>> TaggedItem.objects.filter(content_object=guido)
    # This will also fail
    >>> TaggedItem.objects.get(content_object=guido)

De même, les clés `GenericForeignKey` n’apparaissent pas dans les formulaires `ModelForm`.

### Relations génériques inverses

class GenericRelation
  related_query_name

    La relation de l’objet lié vers cet objet n’existe pas par défaut. En définissant `related_query_name`, la relation est créée de l’objet lié vers celui-ci. Cela permet d’interroger et de filtrer à partir de l’objet lié.

Si vous savez quels seront les modèles que vous utiliserez le plus souvent, vous pouvez également ajouter une relation générique « inverse » pour activer une API supplémentaire. Par exemple :

    from django.contrib.contenttypes.fields import GenericRelation
    from django.db import models

    class Bookmark(models.Model):
        url = models.URLField()
        tags = GenericRelation(TaggedItem)

Les instances `Bookmark` auront chacune un attribut `tags`, qui peut être utilisé pour récupérer leurs objets `TaggedItems` associés :

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

Il est aussi possible d’utiliser `add()`, `create()` ou `set()` pour créer des relations :

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

L’appel à `remove()` va supprimer en bloc les objets de modèle ciblés :

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

La méthode `clear()` peut être utilisée pour supprimer en bloc tous les objets liés à une instance :

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

En définissant l’attribut `related_query_name` de `GenericRelation`, cela permet d’interroger à partir de l’objet lié :

    tags = GenericRelation(TaggedItem, related_query_name="bookmark")

Cela permet de filtrer, trier ou d’autres opérations d’interrogation sur `Bookmark` à partir de `TaggedItem`:

    >>> # Get all tags belonging to bookmarks containing `django` in the url
    >>> TaggedItem.objects.filter(bookmark__url__contains="django")
    <QuerySet [<TaggedItem: django>, <TaggedItem: python>]>

Si vous n’ajoutez pas la relation inverse `related_query_name`, vous pouvez faire les mêmes types de recherches manuellement :

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

Tout comme `GenericForeignKey` accepte les noms des champs type de contenu et identifiant d’object en paramètres, il en va de même pour `GenericRelation`; si le modèle qui a la clé étrangère générique n’utilise pas des noms par défaut pour ces champs, vous devez lui passer les noms des champs lors de la création d’une relation `GenericRelation`. Par exemple, si le modèle `TaggedItem` mentionné ci-dessus utilisait des champs nommés `content_type_fk` et `object_primary_key` pour créer sa clé étrangère générique, alors une relation `GenericRelation` inverse devrait être définie comme suit :

    tags = GenericRelation(
        TaggedItem,
        content_type_field="content_type_fk",
        object_id_field="object_primary_key",
    )

Notez également que si vous supprimez un objet qui possède une relation `GenericRelation`, tous les objets qui ont une clé `GenericForeignKey` pointant vers lui seront aussi supprimés. Dans l’exemple ci-dessus, cela signifie que si un objet `Bookmark` a été supprimé, tous les objets `TaggedItem` pointant dessus seront supprimés en même temps.

Contrairement à `ForeignKey`, `GenericForeignKey` n’accepte pas de paramètre `on_delete` pour personnaliser ce comportement ; si vous le souhaitez, vous pouvez éviter la cascade de suppression en évitant de définir une relation `GenericRelation` ; un comportement différent peut être fourni par le signal `pre_delete`.

### Relations génériques et agrégation

L’API d’agrégation de base de données de Django fonctionne avec les relations `GenericRelation`. Par exemple, vous pouvez savoir combien de « tags » sont définis pour tous les signets :

    >>> Bookmark.objects.aggregate(Count("tags"))
    {'tags__count': 3}

### Relations génériques dans les formulaires

Le module `django.contrib.contenttypes.forms` fournit :

- `BaseGenericInlineFormSet`

- Une fabrique de groupe de formulaires, `generic_inlineformset_factory()`, pour être utilisée avec `GenericForeignKey`.

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)

  Renvoie un `GenericInlineFormSet` à l’aide de `modelformset_factory()`.

  Vous devez fournir `ct_field` et `fk_field` s’ils sont différents des valeurs par défaut, respectivement `content_type` et `object_id`. Les autres paramètres sont similaires à ceux documentés dans `modelformset_factory()` et `inlineformset_factory()`.

  Le paramètre `for_concrete_model` correspond au paramètre `for_concrete_model` de `GenericForeignKey`.

### Relations génériques dans l’interface d’administration

Le module `django.contrib.contenttypes.admin` fournit `GenericTabularInline` et `GenericStackedInline` (sous-classes de `GenericInlineModelAdmin`)

Ces classes et fonctions permettent l’utilisation de relations génériques dans les formulaires et l’interface d’administration. Voir la documentation sur les formulaires groupés de modèle et l’interface d’administration pour plus d’informations.

class GenericInlineModelAdmin

  La classe `GenericInlineModelAdmin` hérite de toutes les propriétés d’une classe `InlineModelAdmin`. Cependant, elle en ajoute quelques autres de son cru pour travailler avec la relation générique :

  ct_field

    Le nom du champ de clé étrangère `ContentType` sur le modèle. Par défaut, `content_type`.

  ct_fk_field

    Le nom du champ nombre entier qui représente l’identifiant de l’objet associé. Par défaut, `object_id`.

class GenericTabularInline

class GenericStackedInline

  Les sous-classes de `GenericInlineModelAdmin` avec mises en page empilées et tabulaires, respectivement.

### `GenericPrefetch()`

class GenericPrefetch(lookup, querysets, to_attr=None)

Cette expression de recherche est semblable à `Prefetch()` mais ne doit être utilisée que pour les relations `GenericForeignKey`. L’argument `querysets` accepte une liste de jeux de requête, chacune pour un type de contenu `ContentType` différent. C’est bien utile pour des jeux de résultats `GenericForeignKey` au contenu hétérogène.

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