Фреймворк contenttypes
Django включает приложение contenttypes, которое может отслеживать все модели, установленные в вашем проекте Django, предоставляя высокоуровневый, общий интерфейс для работы с вашими моделями.
Обзор
В основе приложения contenttypes лежит модель ContentType, которая находится в django.contrib.contenttypes.models.ContentType. Экземпляры ContentType представляют и хранят информацию о моделях, установленных в вашем проекте, и новые экземпляры ContentType автоматически создаются при установке новых моделей.
Экземпляры ContentType имеют методы для возвращения представляемых ими классов моделей и для запроса объектов из этих моделей. ContentType также имеет настраиваемый менеджер, который добавляет методы для работы с ContentType и для получения экземпляров ContentType для конкретной модели.
Связи между вашими моделями и ContentType также могут быть использованы для включения «общих» отношений между экземпляром одной из ваших моделей и экземплярами любой установленной вами модели.
Установка фреймворка contenttypes
Фреймворк contenttypes включён в список по умолчанию INSTALLED_APPS, созданный django-admin startproject, но если вы его удалили или вручную настроили список INSTALLED_APPS, вы можете включить его, добавив 'django.contrib.contenttypes' в ваше значение INSTALLED_APPS.
В целом, рекомендуется иметь установленный фреймворк contenttypes; несколько других включённых приложений Django его требуют:
- Приложение администратора использует его для ведения истории каждого объекта, добавленного или изменённого через интерфейс администратора.
- Django’s
authentication frameworkиспользует его для привязки разрешений пользователей к определённым моделям.
Модель ContentType
-
class ContentType[source] -
Каждый экземпляр
ContentTypeимеет два поля, которые вместе уникально описывают установленную модель:-
app_label -
Имя приложения, к которому принадлежит модель. Оно взято из атрибута
app_labelмодели и включает только последнюю часть пути импорта приложения; например, “django.contrib.contenttypes” превращается вapp_label“contenttypes”.
-
model -
Имя класса модели.
Кроме того, доступно следующее свойство:
-
name -
Читабельное имя типа контента. Оно взято из атрибута
verbose_nameмодели.
-
До Django 1.8 свойство name было реальным полем в модели ContentType.
Рассмотрим пример, чтобы увидеть, как это работает. Если у вас уже установлено приложение contenttypes, а затем вы добавляете the sites application в ваше значение INSTALLED_APPS и запускаете manage.py migrate, чтобы установить его, модель django.contrib.sites.models.Site будет установлена в вашей базе данных. Вместе с ней будет создан новый экземпляр ContentType со следующими значениями:
-
app_labelбудет установлено в'sites'(последняя часть пути импорта “django.contrib.sites”). -
modelбудет установлено в'site'.
Методы экземпляров ContentType
Каждый экземпляр ContentType имеет методы, которые позволяют получить модель, представленную экземпляром ContentType, или извлечь объекты из этой модели:
-
ContentType.get_object_for_this_type(**kwargs)[source] -
Принимает набор допустимых аргументов поиска для модели, которую представляет
ContentType, и выполняетa get() lookupдля этой модели, возвращая соответствующий объект.
-
ContentType.model_class()[source] -
Возвращает класс модели, представленный этим экземпляром
ContentType.
Например, мы можем найти ContentType для модели User:
>>> 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() позволяют использовать два важных случая:
- Используя эти методы, вы можете писать высокоуровневый обобщённый код, который выполняет запросы к любой установленной модели – вместо импорта и использования одного конкретного класса модели, вы можете передать
app_labelиmodelв поискContentTypeво время выполнения и работать с классом модели или извлекать объекты из него. - Вы можете связать другую модель с
ContentTypeкак способ привязки экземпляров к определённым классам моделей, и использовать эти методы для получения доступа к этим классам моделей.
Несколько из встроенных приложений Django используют последний метод. Например, the permissions system в фреймворке аутентификации Django использует модель Permission с внешним ключом к ContentType; это позволяет Permission представлять такие понятия, как «может добавить запись блога» или «может удалить новостную историю».
Менеджер ContentTypeManager
-
class ContentTypeManager[source] -
ContentTypeтакже имеет пользовательский менеджер,ContentTypeManager, который добавляет следующие методы:-
clear_cache()[source] -
Очищает внутренний кэш, используемый
ContentTypeдля отслеживания моделей, для которых он создал экземплярыContentType. Вам, вероятно, никогда не придется вызывать этот метод самостоятельно; Django вызовет его автоматически, когда это потребуется.
-
get_for_id(id)[source] -
Поиск
ContentTypeпо ID. Поскольку этот метод использует тот же общий кэш, что иget_for_model(), предпочтительно использовать этот метод вместо обычногоContentType.objects.get(pk=id)
-
get_for_model(model, for_concrete_model=True)[source] -
Принимает класс модели или экземпляр модели и возвращает экземпляр
ContentType, представляющий эту модель.for_concrete_model=Falseпозволяет извлечьContentTypeпрокси-модели.
-
get_for_models(*models, for_concrete_models=True)[source] -
Принимает переменное количество классов моделей и возвращает словарь, сопоставляющий классы моделей с экземплярами
ContentType, которые их представляют.for_concrete_models=Falseпозволяет извлечьContentTypeпрокси-моделей.
-
get_by_natural_key(app_label, model)[source] -
Возвращает экземпляр
ContentType, однозначно идентифицируемый заданным приложением и именем модели. Основное назначение этого метода заключается в том, чтобы позволить объектамContentTypeссылаться через естественный ключ во время десериализации.
-
Метод get_for_model() особенно полезен, когда вы знаете, что вам нужно работать с ContentType, но не хотите тратить время на получение метаданных модели для выполнения ручного поиска:
>>> from django.contrib.auth.models import User >>> user_type = ContentType.objects.get_for_model(User) >>> user_type <ContentType: user>
Общие отношения
Добавление внешнего ключа от одной из ваших собственных моделей к ContentType позволяет вашей модели эффективно связываться с другим классом модели, как в примере модели Permission выше. Но можно сделать еще один шаг и использовать ContentType для настройки действительно общих (иногда называемых «полиморфных») отношений между моделями.
Простой пример — система тегов, которая может выглядеть так:
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class TaggedItem(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
def __str__(self): # __unicode__ on Python 2
return self.tag
Обычный ForeignKey может «указывать» только на одну другую модель, что означает, что если модель TaggedItem использовала ForeignKey, ей пришлось бы выбрать одну и только одну модель для хранения тегов. Приложение contenttypes предоставляет специальный тип поля (GenericForeignKey) который обходит это ограничение и позволяет установить связь с любой моделью:
-
class GenericForeignKey[source] -
Настройка
GenericForeignKeyсостоит из трех частей:- У вашей модели должен быть
ForeignKeyкContentType. Обычно это поле называется «content_type». - У вашей модели должно быть поле для хранения значений первичных ключей из моделей, с которыми вы будете связаны. Для большинства моделей это означает
PositiveIntegerField. Обычно это поле называется «object_id». - У вашей модели должен быть
GenericForeignKey, и вы должны передать ему имена двух полей, описанных выше. Если эти поля называются «content_type» и «object_id», вы можете этого не делать — это стандартные имена полей, которыеGenericForeignKeyбудет искать.
-
for_concrete_model -
Если
False, поле сможет ссылаться на прокси-модели. По умолчаниюTrue. Это отражает аргументfor_concrete_modelдляget_for_model().
Устарело начиная с версии 1.7: Этот класс был перемещен из
django.contrib.contenttypes.genericвdjango.contrib.contenttypes.fields. Поддержка импорта из старого местоположения будет удалена в Django 1.9. - У вашей модели должен быть
Совместимость типов первичных ключей
Поле «object_id» не обязательно должно иметь тот же тип, что и поля первичного ключа в связанных моделях, но значения их первичных ключей должны быть преобразуемы в тот же тип, что и поле «object_id», с помощью метода get_db_prep_value().
Например, если вы хотите разрешить общие отношения к моделям с полями первичного ключа типа IntegerField или CharField, вы можете использовать CharField для поля «object_id» в вашей модели, так как целые числа могут быть преобразованы в строки методом get_db_prep_value().
Для максимальной гибкости вы можете использовать TextField, у которого нет определенной максимальной длины, однако это может привести к существенным потерям производительности в зависимости от базы данных.
Нет универсального решения для выбора наилучшего типа поля. Вам следует оценить модели, которые, по вашему мнению, будут указывать, и определить, какое решение будет наиболее эффективным для вашего случая использования.
Сериализация ссылок на ContentType объекты
Если вы сериализуете данные (например, при генерации fixtures) из модели, которая реализует общие отношения, вам, вероятно, следует использовать естественный ключ для уникальной идентификации связанных объектов ContentType. См. естественные ключи и dumpdata --natural-foreign для получения дополнительной информации.
Это позволит создать API, аналогичный API для обычной ForeignKey; каждый 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>
Из-за реализации GenericForeignKey вы не можете использовать такие поля непосредственно с фильтрами (filter() и exclude(), например) через API базы данных. Поскольку GenericForeignKey не является обычным объектом поля, эти примеры не будут работать:
# This will fail >>> TaggedItem.objects.filter(content_object=guido) # This will also fail >>> TaggedItem.objects.get(content_object=guido)
Также, GenericForeignKey не отображается в ModelForm.
Обратные общие отношения
-
class GenericRelation[source] -
-
Связь с объектом-ответвлением обратно к этому объекту по умолчанию не существует. Установка
related_query_nameсоздает связь от связанного объекта обратно к этому объекту. Это позволяет выполнять запросы и фильтрацию из связанного объекта.
Устарело начиная с версии 1.7: Этот класс был перемещен из
django.contrib.contenttypes.genericвdjango.contrib.contenttypes.fields. Поддержка импорта из старого местоположения будет удалена в Django 1.9. -
Если вам известны модели, которые вы чаще всего будете использовать, вы также можете добавить обратное общее отношение, чтобы включить дополнительный API. Например:
from django.db import models
from django.contrib.contenttypes.fields import GenericRelation
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() [<TaggedItem: django>, <TaggedItem: python>]
Определение GenericRelation с установленным related_query_name позволяет выполнять запросы из связанного объекта:
tags = GenericRelation(TaggedItem, related_query_name='bookmarks')
Это позволяет выполнять фильтрацию, сортировку и другие операции запросов на Bookmark из TaggedItem:
>>> # Get all tags belonging to bookmarks containing `django` in the url >>> TaggedItem.objects.filter(bookmarks__url__contains='django') [<TaggedItem: django>, <TaggedItem: python>]
Точно так же, как GenericForeignKey принимает имена полей типа содержимого и идентификатора объекта в качестве аргументов, так и 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')
Конечно, если вы не добавите обратное отношение, вы можете выполнить такие же запросы вручную:
>>> b = Bookmark.objects.get(url='https://www.djangoproject.com/') >>> bookmark_type = ContentType.objects.get_for_model(b) >>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id, ... object_id=b.id) [<TaggedItem: django>, <TaggedItem: python>]
Обратите внимание, что если вы удаляете объект, у которого есть GenericRelation, любые объекты, у которых есть ct_field, указывающий на него, также будут удалены. В приведённом выше примере это означает, что если объект Comment был удалён, любые объекты ct_field="object_pk", указывающие на него, будут удалены одновременно.
В отличие от ForeignKey, GenericForeignKey не принимает аргумент on_delete для настройки этого поведения; если требуется, вы можете избежать каскадного удаления, просто не используя GenericRelation, и альтернативное поведение можно предоставить через сигнал pre_delete.
Общие отношения и агрегация
API агрегации баз данных Django работает с GenericRelation. Например, вы можете узнать, сколько тегов имеют все закладки:
>>> Bookmark.objects.aggregate(Count('tags'))
{'tags__count': 3}
Общие отношения в формах
Модуль django.contrib.contenttypes.forms предоставляет:
BaseGenericInlineFormSet- Фабрику формсета,
generic_inlineformset_factory(), для использования сGenericForeignKey.
-
class BaseGenericInlineFormSet[source] -
Устарело начиная с версии 1.7: Этот класс был перемещён из
django.contrib.contenttypes.genericвdjango.contrib.contenttypes.forms. Поддержка импорта из старого расположения будет удалена в Django 1.9.
-
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)[source] -
Возвращает
GenericInlineFormSetс использованиемmodelformset_factory().Вы должны указать
ct_fieldиfk_field, если они отличаются от значений по умолчанию,content_typeиobject_idсоответственно. Другие параметры аналогичны тем, которые описаны вmodelformset_factory()иinlineformset_factory().Аргумент
for_concrete_modelсоответствует аргументуfor_concrete_modelвGenericForeignKey.Устарело начиная с версии 1.7: Эта функция была перемещена из
django.contrib.contenttypes.genericвdjango.contrib.contenttypes.forms. Поддержка импорта из старого расположения будет удалена в Django 1.9.min_numиvalidate_minбыли добавлены.
Общие отношения в админке
Модуль django.contrib.contenttypes.admin предоставляет GenericTabularInline и GenericStackedInline (подклассы GenericInlineModelAdmin)
Эти классы и функции позволяют использовать общие отношения в формах и админке. Для получения дополнительной информации обратитесь к документации по формам на основе моделей и админке.
-
class GenericInlineModelAdmin[source] -
Класс
GenericInlineModelAdminнаследует все свойства из классаInlineModelAdmin. Однако он добавляет несколько собственных свойств для работы с общим отношением:-
ct_field -
Имя поля внешнего ключа
ContentTypeв модели. Значение по умолчанию:content_type.
-
ct_fk_field -
Имя целочисленного поля, представляющего идентификатор связанного объекта. Значение по умолчанию:
object_id.
Устарело начиная с версии 1.7: Этот класс был перемещён из
django.contrib.contenttypes.genericвdjango.contrib.contenttypes.admin. Поддержка импорта из старого расположения будет удалена в Django 1.9. -
-
class GenericTabularInline[source]
-
class GenericStackedInline[source] -
Подклассы
GenericInlineModelAdminс компоновкой "в столбцы" и "в таблицу", соответственно.Устарело начиная с версии 1.7: Эти классы были перемещены из
django.contrib.contenttypes.genericвdjango.contrib.contenttypes.admin. Поддержка импорта из старого расположения будет удалена в Django 1.9.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/ref/contrib/contenttypes/