Django incluye una aplicación contenttypes que puede rastrear todos los modelos instalados en tu proyecto Django, proporcionando una interfaz genérica y alta nivel para trabajar con tus modelos.
En el corazón de la aplicación de contenttypes está el modelo ContentType, que vive en django.contrib.contenttypes.models.ContentType. Las instancias del modelo ContentType representan y almacenan información sobre los modelos instalados en tu proyecto, y se crean nuevas instancias de ContentType automáticamente cada vez que se instalan nuevos modelos.
Las instancias del modelo ContentType tienen métodos para devolver las clases de modelo que representan y para consultar objetos de esos modelos. El modelo ContentType también tiene un administrador personalizado que agrega métodos para trabajar con el modelo ContentType y obtener instancias del modelo ContentType para un modelo en particular.
Las relaciones entre tus modelos y ContentType también se pueden utilizar para habilitar «relaciones genéricas» entre una instancia de uno de tus modelos y las instancias de cualquier modelo que tengas instalado.
El framework contenttypes está incluido en la lista por defecto INSTALLED_APPS creada por django-admin startproject, pero si lo has eliminado o si estableciste manualmente tu lista de INSTALLED_APPS, puedes habilitarlo agregando 'django.contrib.contenttypes' a tu configuración de INSTALLED_APPS.
En general, es una buena idea tener instalado el framework contenttypes; varias de las aplicaciones incluidas en Django requieren que esté instalado:
La aplicación administrativa lo utiliza para registrar la historia de cada objeto agregado o modificado a través de la interfaz administrativa.
El marco de autenticación de Django <django.contrib.auth> utiliza el framework contenttypes para vincular las permisos de usuario a modelos específicos.
Cada instancia de ContentType tiene dos campos que, tomados en conjunto, describen de manera única un modelo instalado:
El nombre del paquete al que pertenece el modelo. Esto se toma del atributo app_label del modelo y solo incluye la última parte del camino de importación Python del paquete; django.contrib.contenttypes, por ejemplo, se convierte en un app_label de contenttypes.
El nombre de la clase del modelo.
Además, la siguiente propiedad está disponible:
El nombre legible por humanos del tipo de contenido. Este se toma del atributo verbose_name del modelo.
Vamos a ver un ejemplo para saber cómo funciona esto. Si ya tienes instalada la aplicación contenttypes, y luego agregas la aplicación sitios a tu configuración de INSTALLED_APPS`<setting> y ejecutas ``manage.py migrate` para instalarla, el modelo django.contrib.sites.models.Site se instalará en tu base de datos. Con él, también se creará una nueva instancia del tipo ContentType con los siguientes valores:
Cada instancia de ContentType tiene métodos que permiten obtener desde una instancia de ContentType hasta el modelo que representa, o recuperar objetos de ese modelo:
Toma un conjunto de argumentos de búsqueda válidos lookup arguments para el modelo que representa la clase ~django.contrib.contenttypes.models.ContentType, y realiza una búsqueda get() lookup en ese modelo, devolviendo el objeto correspondiente. El argumento using se puede utilizar para especificar un diferente a la base de datos por defecto.
La traducción es:
Devuelve la clase del modelo representada por esta instancia de ContentType.
Por ejemplo, podríamos buscar el ContentType para el modelo User:
>>> from django.contrib.contenttypes.models import ContentType
>>> user_type = ContentType.objects.get(app_label="auth", model="user")
>>> user_type
<ContentType: user>
Y luego utilizarlo para consultar un usuario particular User, o para obtener acceso a la clase del modelo User:
>>> user_type.model_class()
<class 'django.contrib.auth.models.User'>
>>> user_type.get_object_for_this_type(username="Guido")
<User: Guido>
Juntos, get_object_for_this_type() y model_class() permiten dos casos de uso extremadamente importantes:
Al utilizar estos métodos, puedes escribir código genérico a nivel alto que realiza consultas en cualquier modelo instalado – en lugar de importar y usar una clase de modelo específica, puedes pasar un app_label y model en un lookup de ContentType en tiempo de ejecución, y luego trabajar con la clase del modelo o recuperar objetos de ella:
Puedes relacionar otro modelo con ContentType como una forma de vincular instancias de él a clases de modelos particulares, y utilizar estos métodos para obtener acceso a esas clases de modelo:
Algunas de las aplicaciones incluidas en Django utilizan la técnica del último tipo. Por ejemplo, el sistema de permisos <django.contrib.auth.models.Permission> en el marco de autenticación de Django utiliza un modelo Permission con una clave foránea a ContentType; esto permite que Permission represente conceptos como «puedes agregar entrada del blog» o «puedes eliminar historia de noticias»:
ContentTypeManager¶ContentType también tiene un administrador personalizado, ContentTypeManager, que agrega los siguientes métodos:
Limpia una caché interna utilizada por ContentType para mantener el seguimiento de modelos para los cuales ha creado instancias de ContentType. Es probable que nunca necesites llamar a este método tú mismo; Django lo llamará automáticamente cuando sea necesario:
Busca un ContentType por ID. Dado que este método utiliza la misma caché compartida como get_for_model(), se prefiere utilizar este método sobre el usual ContentType.objects.get(pk=id)
Takes either a model class or an instance of a model, y devuelve la instancia de ContentType que representa ese modelo. for_concrete_model=False permite obtener la ContentType de un modelo proxy.
Toma una cantidad variable de clases de modelos, y devuelve un diccionario que mapea las clases de modelos a las instancias de ContentType que las representan. for_concrete_models=False permite obtener la ContentType de modelos proxy.
Devuelve la instancia de ContentType única identificada por el etiqueta de aplicación y nombre del modelo dados. El propósito principal de este método es permitir que los objetos ContentType se refieran mediante una clave natural <topics-serialization-natural-keys> durante la deserialización.
El método get_for_model() es especialmente útil cuando sabes que necesitas trabajar con un ContentType pero no quieres ir a buscar los metadatos del modelo para realizar una búsqueda manual:
>>> from django.contrib.auth.models import User
>>> ContentType.objects.get_for_model(User)
<ContentType: user>
Agregar una clave foránea desde uno de tus modelos propios a ContentType permite que tu modelo se atee efectivamente a otra clase de modelo, como en el ejemplo del modelo Permission anterior. Pero es posible ir un paso más allá y utilizar ContentType para habilitar relaciones verdaderamente genéricas (a veces llamadas «polimórficas») entre modelos.
Por ejemplo, podría usarse en un sistema de etiquetas como se muestra a continuación:
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"]),
]
Una relación normal ForeignKey solo puede «apuntar» a otro modelo, lo que significa que si el modelo TaggedItem utilizara una ForeignKey tendría que elegir un y sólo un modelo para almacenar etiquetas. La aplicación contenttypes proporciona un tipo de campo especial (GenericForeignKey) que supera esto y permite la relación con cualquier modelo:
Hay tres partes en configurar una GenericForeignKey:
Dale a tu modelo una ForeignKey a ContentType. El nombre usual para este campo es «content_type».
Dale a tu modelo un campo que pueda almacenar valores de clave primaria de los modelos con los que estarás relacionando. Para la mayoría de los modelos, esto significa un PositiveBigIntegerField. El nombre habitual para este campo es «object_id».
Dale a tu modelo un GenericForeignKey, y pásaselo los nombres de los dos campos descritos anteriormente. Si estos campos se llaman «content_type» y «object_id», puedes omitir esto – esos son los nombres de campo por defecto que buscará GenericForeignKey.
A diferencia de para la clase ~django.db.models.ForeignKey, no se crea automáticamente un índice en la base de datos sobre la clase ~django.contrib.contenttypes.fields.GenericForeignKey, por lo que se recomienda utilizar el atributo <django.db.models.Options.indexes> para agregar tu propio índice múltiple. Este comportamiento puede cambiar en el futuro (ticket #23435).
Si Falso, el campo podrá referenciar modelos de proxy. Por defecto es Verdadero. Esto refleja el argumento for_concrete_model del método get_for_model().
Compatibilidad del tipo de clave primaria
El campo «object_id» no tiene que ser del mismo tipo que los campos primarios de los modelos relacionados, pero sus valores primarios deben poder coercerse al mismo tipo que el campo «object_id» mediante su método get_db_prep_value().
Ejemplo: si deseas permitir relaciones generales a modelos con campos de clave primaria que sean IntegerField o CharField, puedes utilizar CharField para el campo «object_id» en tu modelo, ya que los enteros pueden ser coercidos a cadenas por get_db_prep_value().
Para máxima flexibilidad puedes utilizar un TextField, que no tiene una longitud máxima definida, aunque esto puede acarrear importantes penalizaciones de rendimiento dependiendo del motor de base de datos utilizado.
No existe una solución «todo en uno» para determinar qué tipo de campo es el mejor. Debes evaluar los modelos a los que esperas apuntar y determinar cuál será la solución más efectiva para tu caso de uso.
Serializando referencias a objetos ContentType
Si estás serializando datos (por ejemplo, cuando se generan fixtures) desde un modelo que implementa relaciones genéricas, probablemente deberías utilizar una clave natural para identificar de manera única objetos relacionados con el tipo de contenido ContentType. Consulte claves naturales y la opción dumpdata –natural-foreign para obtener más información.
Esto habilitará una API similar a la utilizada para un campo de referencia normal ForeignKey; cada TaggedItem tendrá un campo content_object que devuelve el objeto al que está relacionado, y también puedes asignar a ese campo o utilizarlo cuando se crea 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 el objeto relacionado es eliminado, los campos content_type y object_id permanecen configurados con sus valores originales y el campo de referencia genérico devuelve None:
>>> guido.delete()
>>> t.content_object # returns None
Debido a la forma en que está implementada GenericForeignKey, no puedes utilizar tales campos directamente con filtros (por ejemplo, filter() y exclude(), etc.) mediante la API de base de datos. Porque un campo de referencia genérico GenericForeignKey no es un objeto de campo normal, estos ejemplos no funcionarán:
# This will fail
>>> TaggedItem.objects.filter(content_object=guido)
# This will also fail
>>> TaggedItem.objects.get(content_object=guido)
Del mismo modo, los campos de referencia genérica GenericForeignKeys no aparecen en las formas de modelo ModelForm.
La relación del objeto relacionado hacia este objeto no existe por defecto. Establecer related_query_name crea una relación desde el objeto relacionado hacia este uno. Esto permite consultar y filtrar desde el objeto relacionado.
Si sabes qué modelos utilizarás con mayor frecuencia, también puedes agregar una «relación genérica inversa» para habilitar una API adicional. Por ejemplo:
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
class Bookmark(models.Model):
url = models.URLField()
tags = GenericRelation(TaggedItem)
Las instancias de Bookmark tendrán cada una un atributo tags, que se puede utilizar para recuperar sus TaggedItems asociados:
>>> 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>]>
También puedes utilizar add(), create() o set() para crear relaciones:
>>> 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>]>
La traducción de los textos es la siguiente:
>>> b.tags.remove(t3)
>>> b.tags.all()
<QuerySet [<TaggedItem: django>]>
>>> TaggedItem.objects.all()
<QuerySet [<TaggedItem: django>]>
El método clear() se puede utilizar para eliminar todos los objetos relacionados para una instancia en masa:
>>> b.tags.clear()
>>> b.tags.all()
<QuerySet []>
>>> TaggedItem.objects.all()
<QuerySet []>
Definir GenericRelation con related_query_name establecido permite consultar desde el objeto relacionado:
tags = GenericRelation(TaggedItem, related_query_name="bookmark")
Esto habilita la filtración, ordenación y otras operaciones de consulta sobre Bookmark desde 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 no agregas related_query_name, puedes realizar los mismos tipos de consultas manualmente:
>>> 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>]>
Del mismo modo que GenericForeignKey acepta los nombres de los campos de tipo de contenido y ID del objeto como argumentos, así también lo hace GenericRelation; si el modelo que tiene la clave foránea genérica utiliza nombres no predeterminados para esos campos, debes pasar los nombres de los campos cuando se configura una GenericRelation a él. Por ejemplo, si el modelo TaggedItem referido anteriormente utilizaba campos llamados content_type_fk y object_primary_key para crear su clave foránea genérica, entonces una GenericRelation hacia él necesitaría ser definida de la siguiente manera:
tags = GenericRelation(
TaggedItem,
content_type_field="content_type_fk",
object_id_field="object_primary_key",
)
También ten en cuenta que si eliminas un objeto que tiene una GenericRelation, cualquier objeto que tenga una GenericForeignKey apuntando a él se eliminará también. En el ejemplo anterior, esto significa que si se eliminara un objeto Bookmark, los objetos TaggedItem que apuntan a él se eliminarían al mismo tiempo.
A diferencia de ForeignKey, GenericForeignKey no acepta un argumento on_delete para personalizar este comportamiento; si lo deseas, puedes evitar la eliminación en cascada evitando el uso de GenericRelation y proporcionar un comportamiento alternativo a través del pre_delete signal.
La API de Django para la agregación de bases de datos funciona con una GenericRelation. Por ejemplo, puedes encontrar cuántos etiquetas tienen todos los marcadores:
>>> Bookmark.objects.aggregate(Count("tags"))
{'tags__count': 3}
El módulo django.contrib.contenttypes.forms proporciona:
Una clase de formset base, BaseGenericInlineFormSet
Una fábrica de formsetes generales, generic_inlineformset_factory(), para usar con GenericForeignKey.
Devuelve un GenericInlineFormSet utilizando modelformset_factory().
Debes proporcionar ct_field y fk_field si son diferentes de los valores por defecto, content_type y object_id respectivamente. Los otros parámetros son similares a los documentados en modelformset_factory() y inlineformset_factory().
El argumento for_concrete_model corresponde al argumento for_concrete_model del GenericForeignKey.
El módulo django.contrib.contenttypes.admin proporciona GenericTabularInline y GenericStackedInline (subclases de GenericInlineModelAdmin)
Estas clases y funciones permiten el uso de relaciones generales en formularios y la administración. Consulte la documentación sobre formset de modelos y administración para obtener más información.
La traducción de los textos es la siguiente:
El nombre del campo clave foránea de tipo ContentType en el modelo. Por defecto es content_type.
El nombre del campo entero que representa el ID del objeto relacionado. Por defecto es object_id.
Subclases de GenericInlineModelAdmin con diseños apilados y tabulares, respectivamente.
GenericPrefetch()¶Esta búsqueda es similar a Prefetch() y solo debe ser utilizada en GenericForeignKey. El argumento querysets acepta una lista de conjuntos de consultas, cada uno para un diferente ContentType. Esto es útil para GenericForeignKey con un conjunto no homogéneo de resultados.
>>> 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>]>
may 31, 2026