Investigamos en distintos espacios para tenerte la respuesta para tu inquietud, si tienes alguna duda deja tu inquietud y te contestaremos con gusto.
Django incluye un contenttypes
aplicación que puede rastrear todos los modelos instalados en su proyecto impulsado por Django, proporcionando una interfaz genérica de alto nivel para trabajar con sus modelos.
Visión general
En el corazón de la aplicación contenttypes está el ContentType
modelo, que vive en django.contrib.contenttypes.models.ContentType
. Instancias de ContentType
representar y almacenar información sobre los modelos instalados en su proyecto y nuevas instancias de ContentType
se crean automáticamente cada vez que se instalan nuevos modelos.
Instancias de ContentType
tienen métodos para devolver las clases de modelo que representan y para consultar objetos de esos modelos. ContentType
también tiene un administrador personalizado que agrega métodos para trabajar con ContentType
y para obtener instancias de ContentType
para un modelo en particular.
Relaciones entre sus modelos y ContentType
también se puede utilizar para habilitar relaciones “genéricas” entre una instancia de uno de sus modelos y las instancias de cualquier modelo que haya instalado.
Instalación del marco contenttypes
El marco de tipos de contenido se incluye en el INSTALLED_APPS
lista creada por django-admin startproject
, pero si lo eliminó o si configuró manualmente su INSTALLED_APPS
lista, puede habilitarla agregando 'django.contrib.contenttypes'
para usted INSTALLED_APPS
configuración.
Por lo general, es una buena idea tener instalado el framework contenttypes; varias de las otras aplicaciones empaquetadas de Django lo requieren:
- La aplicación de administración lo usa para registrar el historial de cada objeto agregado o cambiado a través de la interfaz de administración.
- De Django
authentication framework
lo usa para vincular los permisos de usuario a modelos específicos.
los ContentType
modelo
class ContentType
-
Cada instancia de
ContentType
tiene dos campos que, en conjunto, describen de forma única un modelo instalado:app_label
-
El nombre de la aplicación de la que forma parte el modelo. Esto se toma de la
app_label
atributo del modelo, e incluye solo el último parte de la ruta de importación de Python de la aplicación;django.contrib.contenttypes
, por ejemplo, se convierte en unapp_label
decontenttypes
.
model
-
El nombre de la clase de modelo.
Además, la siguiente propiedad está disponible:
name
-
El nombre legible por humanos del tipo de contenido. Esto se toma de la
verbose_name
atributo del modelo.
Veamos un ejemplo para ver cómo funciona. Si ya tienes el contenttypes
aplicación instalada y luego agregue the sites application
para usted INSTALLED_APPS
configurar y ejecutar manage.py migrate
para instalarlo, el modelo django.contrib.sites.models.Site
se instalará en su base de datos. Junto con él, una nueva instancia de ContentType
se creará con los siguientes valores:
app_label
se establecerá en'sites'
(la última parte de la ruta de Pythondjango.contrib.sites
).model
se establecerá en'site'
.
Métodos en ContentType
instancias
Cada ContentType
instancia tiene métodos que le permiten obtener de una ContentType
instancia al modelo que representa, o para recuperar objetos de ese modelo:
ContentType.get_object_for_this_type(**kwargs)
-
Toma un conjunto de válidos buscar argumentos para el modelo el
ContentType
representa y hacea get() lookup
en ese modelo, devolviendo el objeto correspondiente.
ContentType.model_class()
-
Devuelve la clase de modelo representada por este
ContentType
ejemplo.
Por ejemplo, podríamos buscar el ContentType
Para el User
modelo:
>>>from django.contrib.contenttypes.models import ContentType >>> user_type = ContentType.objects.get(app_label='auth', model='user')>>> user_type <ContentType: user>
Y luego utilícelo para realizar consultas sobre un particular User
, o para acceder a la User
clase modelo:
>>> 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()
habilitar dos casos de uso extremadamente importantes:
- Con estos métodos, puede escribir código genérico de alto nivel que realiza consultas en cualquier modelo instalado; en lugar de importar y usar una única clase de modelo específica, puede pasar una
app_label
ymodel
en unContentType
buscar en tiempo de ejecución y luego trabajar con la clase modelo o recuperar objetos de ella. - Puedes relacionar otro modelo con
ContentType
como una forma de vincular instancias de él a clases de modelo particulares, y use estos métodos para obtener acceso a esas clases de modelo.
Varias de las aplicaciones empaquetadas de Django hacen uso de esta última técnica. Por ejemplo, the permissions system
en el marco de autenticación de Django usa un Permission
modelo con una clave externa para ContentType
; esto permite Permission
representan conceptos como “puede agregar una entrada de blog” o “puede eliminar una noticia”.
los ContentTypeManager
class ContentTypeManager
-
ContentType
también tiene un administrador personalizado,ContentTypeManager
, que agrega los siguientes métodos:clear_cache()
-
Borra una caché interna utilizada por
ContentType
para realizar un seguimiento de los modelos para los que ha creadoContentType
instancias. Probablemente nunca necesitará llamar a este método usted mismo; Django lo llamará automáticamente cuando sea necesario.
get_for_id(id)
-
Buscar un
ContentType
por ID. Dado que este método utiliza la misma caché compartida queget_for_model()
, se prefiere utilizar este método sobre el habitualContentType.objects.get(pk=id)
get_for_model(model, for_concrete_model=True)
-
Toma una clase de modelo o una instancia de un modelo y devuelve el
ContentType
instancia que representa ese modelo.for_concrete_model=False
permite buscar elContentType
de un modelo de proxy.
get_for_models(*models, for_concrete_models=True)
-
Toma un número variable de clases de modelo y devuelve un diccionario que asigna las clases de modelo a la
ContentType
instancias que los representan.for_concrete_models=False
permite buscar elContentType
de modelos proxy.
get_by_natural_key(app_label, model)
-
Devuelve el
ContentType
instancia identificada de forma única por la etiqueta de aplicación y el nombre del modelo dados. El propósito principal de este método es permitirContentType
objetos a los que se hace referencia a través de un clave natural durante la deserialización.
los get_for_model()
El método es especialmente útil cuando sabes que necesitas trabajar con un ContentType
pero no quiero tomarse la molestia de obtener 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>
Relaciones genéricas
Agregar una clave externa de uno de sus propios modelos a ContentType
permite que su modelo se vincule efectivamente a otra clase de modelo, como en el ejemplo de la Permission
modelo anterior. Pero es posible ir un paso más allá y usar ContentType
para permitir relaciones verdaderamente genéricas (a veces llamadas “polimórficas”) entre modelos.
Por ejemplo, podría usarse para un sistema de etiquetado como este:
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import models classTaggedItem(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
Un normal ForeignKey
sólo puede “apuntar a” otro modelo, lo que significa que si el TaggedItem
modelo utilizó un ForeignKey
tendría que elegir uno y solo un modelo para almacenar etiquetas. La aplicación contenttypes proporciona un tipo de campo especial (GenericForeignKey
) que soluciona esto y permite que la relación sea con cualquier modelo:
class GenericForeignKey
-
Hay tres partes para configurar un
GenericForeignKey
:- Dale a tu modelo un
ForeignKey
paraContentType
. El nombre habitual de este campo es “content_type”. - Proporcione a su modelo un campo que pueda almacenar valores de clave primaria de los modelos con los que se relacionará. Para la mayoría de los modelos, esto significa
PositiveIntegerField
. El nombre habitual de este campo es “object_id”. - Dale a tu modelo un
GenericForeignKey
y pásele los nombres de los dos campos descritos anteriormente. Si estos campos se denominan “content_type” y “object_id”, puede omitirlos; esos son los nombres de campo predeterminados.GenericForeignKey
buscará.
for_concrete_model
-
Si
False
, el campo podrá hacer referencia a modelos de proxy. El valor predeterminado esTrue
. Esto refleja elfor_concrete_model
argumento paraget_for_model()
.
- Dale a tu modelo un
Compatibilidad del tipo de clave principal
El campo “object_id” no tiene que ser del mismo tipo que los campos de clave principal en los modelos relacionados, pero sus valores de clave principal deben ser coercibles al mismo tipo que el campo “object_id” por su get_db_prep_value()
método.
Por ejemplo, si desea permitir relaciones genéricas a modelos con IntegerField
o CharField
campos de clave primaria, puede utilizar CharField
para el campo “object_id” en su modelo, ya que los números enteros se pueden convertir en cadenas mediante get_db_prep_value()
.
Para una máxima flexibilidad, puede utilizar un TextField
que no tiene una longitud máxima definida, sin embargo, esto puede incurrir en importantes penalizaciones de rendimiento según el backend de su base de datos.
No existe una solución única que se adapte a todos para determinar cuál es el mejor tipo de campo. Debe evaluar los modelos a los que espera apuntar y determinar qué solución será más efectiva para su caso de uso.
Serializar referencias a ContentType
objetos
Si está serializando datos (por ejemplo, al generar fixtures
) de un modelo que implementa relaciones genéricas, probablemente debería utilizar una clave natural para identificar de forma única ContentType
objetos. Ver claves naturales y dumpdata --natural-foreign
para más información.
Esto habilitará una API similar a la que se usa para un ForeignKey
; cada TaggedItem
tendrá un content_object
campo que devuelve el objeto con el que está relacionado, y también puede asignar a ese campo o usarlo al crear 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 se elimina el objeto relacionado, el content_type
y object_id
los campos permanecen configurados en sus valores originales y el GenericForeignKey
devoluciones None
:
>>> guido.delete()>>> t.content_object # returns None
Debido a la forma GenericForeignKey
está implementado, no puede utilizar dichos campos directamente con filtros (filter()
y exclude()
, por ejemplo) a través de la API de la base de datos. Porque un GenericForeignKey
no es un objeto de campo normal, estos ejemplos no trabaja:
# This will fail>>> TaggedItem.objects.filter(content_object=guido)# This will also fail>>> TaggedItem.objects.get(content_object=guido)
Igualmente, GenericForeignKey
s no aparece en ModelForm
s.
Relaciones genéricas inversas
class GenericRelation
-
related_query_name
-
La relación del objeto relacionado con este objeto no existe de forma predeterminada. Configuración
related_query_name
crea una relación entre el objeto relacionado y este. Esto permite consultar y filtrar desde el objeto relacionado.
Si sabe qué modelos utilizará con más frecuencia, también puede 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 classBookmark(models.Model): url = models.URLField() tags = GenericRelation(TaggedItem)
Bookmark
instancias cada una tendrá un tags
atributo, que se puede utilizar para recuperar su asociado 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>]>
También puedes usar 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>]>
los remove()
La llamada eliminará de forma masiva los objetos de modelo especificados:
>>> b.tags.remove(t3)>>> b.tags.all()<QuerySet [<TaggedItem: django>]>>>> TaggedItem.objects.all()<QuerySet [<TaggedItem: django>]>
los clear()
El método se puede utilizar para eliminar de forma masiva todos los objetos relacionados para una instancia:
>>> b.tags.clear()>>> b.tags.all()<QuerySet []>>>> TaggedItem.objects.all()<QuerySet []>
Definiendo GenericRelation
con related_query_name
set permite realizar consultas desde el objeto relacionado:
tags = GenericRelation(TaggedItem, related_query_name='bookmark')
Esto permite filtrar, ordenar y otras operaciones de consulta en Bookmark
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 no agrega el related_query_name
, puede hacer los mismos tipos de búsquedas 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>]>
Tal como GenericForeignKey
acepta los nombres de los campos de tipo de contenido e ID de objeto como argumentos, también lo hace GenericRelation
; Si el modelo que tiene la clave externa genérica utiliza nombres no predeterminados para esos campos, debe pasar los nombres de los campos al configurar un GenericRelation
lo. Por ejemplo, si el TaggedItem
modelo referido a los campos usados anteriormente nombrados content_type_fk
y object_primary_key
para crear su clave externa genérica, luego un GenericRelation
volver a él tendría que definirse así:
tags = GenericRelation( TaggedItem, content_type_field='content_type_fk', object_id_field='object_primary_key',)
Tenga en cuenta también que si elimina un objeto que tiene un GenericRelation
, cualquier objeto que tenga un GenericForeignKey
que apunte a ella también se eliminará. En el ejemplo anterior, esto significa que si un Bookmark
objeto fueron eliminados, cualquier TaggedItem
los objetos que apuntan a ella se eliminarán al mismo tiempo.
diferente a ForeignKey
, GenericForeignKey
no acepta un on_delete
argumento para personalizar este comportamiento; si lo desea, puede evitar la eliminación en cascada al no usar GenericRelation
, y se puede proporcionar un comportamiento alternativo a través del pre_delete
señal.
Relaciones genéricas y agregación
API de agregación de bases de datos de Django trabaja con un GenericRelation
. Por ejemplo, puede averiguar cuántas etiquetas tienen todos los marcadores:
>>> Bookmark.objects.aggregate(Count('tags'))'tags__count':3
Relación genérica en formas
los django.contrib.contenttypes.forms
módulo proporciona:
BaseGenericInlineFormSet
- Una fábrica de formset,
generic_inlineformset_factory()
, para usar conGenericForeignKey
.
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)
-
Devuelve un
GenericInlineFormSet
utilizandomodelformset_factory()
.Debes proveer
ct_field
yfk_field
si son diferentes de los predeterminados,content_type
yobject_id
respectivamente. Otros parámetros son similares a los documentados enmodelformset_factory()
yinlineformset_factory()
.los
for_concrete_model
El argumento corresponde alfor_concrete_model
argumento sobreGenericForeignKey
.Cambiado en Django 3.2:
los
absolute_max
ycan_delete_extra
Se agregaron argumentos.
Relaciones genéricas en administración
los django.contrib.contenttypes.admin
el módulo proporciona GenericTabularInline
y GenericStackedInline
(subclases de GenericInlineModelAdmin
)
Estas clases y funciones permiten el uso de relaciones genéricas en formularios y el administrador. Ver el modelo de conjunto de formularios y administración documentación para obtener más información.
class GenericInlineModelAdmin
-
los
GenericInlineModelAdmin
La clase hereda todas las propiedades de unaInlineModelAdmin
clase. Sin embargo, agrega algunos propios para trabajar con la relación genérica:ct_field
-
El nombre de
ContentType
campo de clave externa en el modelo. Predeterminado acontent_type
.
ct_fk_field
-
El nombre del campo entero que representa el ID del objeto relacionado. Predeterminado a
object_id
.
class GenericTabularInline
class GenericStackedInline
-
Subclases de
GenericInlineModelAdmin
con diseños apilados y tabulares, respectivamente.