Фреймворк contenttypes

Django включает contenttypesприложение, которое может отслеживать все модели, установленные в вашем проекте на базе Django, обеспечивая высокоуровневый универсальный интерфейс для работы с вашими моделями.

Обзор

В основе приложения contenttypes лежит ContentTypeмодель, в которой живет django.contrib.contenttypes.models.ContentType. Экземпляры ContentTypeпредставляют и хранят информацию о моделях, установленных в вашем проекте, а новые экземпляры ContentTypeавтоматически создаются при установке новых моделей.

Экземпляры ContentTypeимеют методы для возврата классов моделей, которые они представляют, и для запроса объектов из этих моделей. ContentType также есть настраиваемый менеджер, который добавляет методы для работы ContentTypeи получения экземпляров ContentType для конкретной модели.

Отношения между вашими моделями, а ContentTypeтакже могут использоваться для включения «общих» отношений между экземпляром одной из ваших моделей и экземплярами любой установленной вами модели.

Установка фреймворка contenttypes

Инфраструктура типов контента включена в INSTALLED_APPSсписок по умолчанию, созданный пользователем , но если вы удалили ее или вручную настроили свой список, вы можете включить ее, добавив в свой параметр.django-admin startprojectINSTALLED_APPS'django.contrib.contenttypes'INSTALLED_APPS

Обычно рекомендуется установить фреймворк contenttypes; это требуется для некоторых других приложений Django:

  • Приложение администратора использует его для регистрации истории каждого объекта, добавленного или измененного через интерфейс администратора.
  • Django использует его для привязки разрешений пользователей к определенным моделям.authentication framework

ContentTypeМодель

класс ContentType

Каждый экземпляр ContentType имеет два поля, которые, вместе взятые, однозначно описывают установленную модель:

app_label

Имя приложения, частью которого является модель. Это берется из app_labelатрибута модели и включает только последнюю часть пути импорта Python приложения; django.contrib.contenttypes, например, становится app_labelоф contenttypes.

model

Имя класса модели.

Дополнительно доступно следующее свойство:

name

Удобочитаемое имя типа контента. Это взято из verbose_name атрибута модели.

Давайте посмотрим на пример, чтобы увидеть, как это работает. Если у вас уже установлено contenttypesприложение, а затем добавьте его в настройки и запустите для его установки, модель будет установлена ​​в вашу базу данных. Вместе с этим будет создан новый экземпляр со следующими значениями:the sites applicationINSTALLED_APPSmanage.py migratedjango.contrib.sites.models.SiteContentType

  • app_label будет установлено значение 'sites'(последняя часть пути Python django.contrib.sites).
  • model будет установлен на 'site'.

Методы на ContentTypeинстансах

У каждого ContentTypeэкземпляра есть методы, которые позволяют вам перейти от ContentTypeэкземпляра к модели, которую он представляет, или получить объекты из этой модели:

ContentType.get_object_for_this_type( ** kwargs )

Принимает набор допустимых аргументов поиска для модели, которую ContentType представляет, и выполняет для этой модели, возвращая соответствующий объект.a get() lookup

ContentType.model_class()

Возвращает класс модели, представленный этим 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()позволяют реализовать два чрезвычайно важных варианта использования:

  1. Используя эти методы, вы можете написать общий код высокого уровня, который выполняет запросы к любой установленной модели - вместо импорта и использования одного конкретного класса модели вы можете передать app_labelи modelв ContentTypeпоиск во время выполнения, а затем работать с классом модели или извлекать из него объекты.
  2. Вы можете связать другую модель ContentTypeкак способ привязки ее экземпляров к определенным классам модели и использовать эти методы для получения доступа к этим классам модели.

Некоторые из приложений, входящих в пакет Django, используют последний метод. Например, в платформе аутентификации Django используется модель с внешним ключом для ; это позволяет представить такие понятия, как «можно добавить запись в блог» или «можно удалить новость».the permissions systemPermissionContentTypePermission

ContentTypeManager

класс ContentTypeManager

ContentTypeтакже есть собственный менеджер ContentTypeManager, который добавляет следующие методы:

clear_cache()

Очищает внутренний кеш, используемый ContentTypeдля отслеживания моделей, для которых были созданы ContentTypeэкземпляры. Вам, вероятно, никогда не понадобится вызывать этот метод самостоятельно; Django будет вызывать его автоматически, когда это необходимо.

get_for_id( id )

Поиск ContentTypeпо идентификатору. Поскольку этот метод использует тот же общий кеш get_for_model(), что и, предпочтительнее использовать этот метод, чем обычный ContentType.objects.get(pk=id)

get_for_model( модель , for_concrete_model = True )

Принимает либо класс модели, либо экземпляр модели и возвращает ContentTypeэкземпляр, представляющий эту модель. for_concrete_model=Falseпозволяет ContentTypeполучить прокси-модель.

get_for_models( * модели , for_concrete_models = True )

Принимает переменное количество классов модели и возвращает словарь, сопоставляющий классы модели с ContentTypeэкземплярами, представляющими их. for_concrete_models=Falseпозволяет получать ContentTypeпрокси-модели.

get_by_natural_key( app_label , модель )

Возвращает ContentType экземпляр, однозначно идентифицируемый по заданной метке приложения и названию модели. Основная цель этого метода - дать ContentTypeвозможность ссылаться на объекты через естественный ключ во время десериализации.

Этот get_for_model()метод особенно полезен, когда вы знаете, что вам нужно работать с a, ContentTypeно не хотите тратить время на получение метаданных модели для выполнения поиска вручную:

>>> 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.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    def __str__(self):
        return self.tag

Обычный ForeignKeyможет «указывать» только на одну другую модель, что означает, что если бы TaggedItemмодель использовала a, ForeignKeyей пришлось бы выбрать одну и только одну модель для хранения тегов. Приложение contenttypes предоставляет специальный тип поля ( GenericForeignKey), который позволяет обойти это и позволяет устанавливать отношения с любой моделью:

класс GenericForeignKey

Настройка состоит из трех частей GenericForeignKey:

  1. Дайте вашу модель есть ForeignKey в ContentType. Обычное имя для этого поля - «content_type».
  2. Дайте вашей модели поле, в котором могут храниться значения первичных ключей из моделей, к которым вы будете относиться. Для большинства моделей это означает PositiveIntegerField. Обычное имя для этого поля - «object_id».
  3. Дайте вашей модели a GenericForeignKeyи передайте ей имена двух полей, описанных выше. Если эти поля названы «content_type» и «object_id», вы можете пропустить это - это имена полей по умолчанию, которые GenericForeignKeyбудут искать.
for_concrete_model

Если False, поле сможет ссылаться на прокси-модели. По умолчанию True. Это отражает for_concrete_modelаргумент get_for_model().

Совместимость типов первичного ключа

Поле «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, аналогичный тому, который используется для обычного 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>

Если соответствующий объект удаляется, то content_typeи object_idполя остаются установленными в их исходные значения и GenericForeignKeyвозвращает None:

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

Из-за того, как GenericForeignKey реализован способ , вы не можете использовать такие поля напрямую с фильтрами ( filter() и exclude(), например) через API базы данных. Поскольку a GenericForeignKeyне является обычным полевым объектом, эти примеры не будут работать:

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

Точно так же GenericForeignKeys не появляется в ModelForms.

Обратные отношения общего положения

класс 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 []>

Определение GenericRelationс помощью related_query_nameset позволяет запрашивать из связанного объекта:

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

Это позволяет выполнять фильтрацию, упорядочивание и другие операции запросов на Bookmark from TaggedItem:

>>> # 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 принимает имена полей контента типа и объект-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указывающие на него объекты будут удалены одновременно.

В отличие от 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( 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 )

Возвращает GenericInlineFormSetusing modelformset_factory().

Необходимо указать ct_fieldи, fk_fieldесли они отличаются от значений по умолчанию, content_typeи object_idсоответственно. Остальные параметры аналогичны параметрам, задокументированным в modelformset_factory()и inlineformset_factory().

for_concrete_modelАргумент соответствует for_concrete_model аргументу GenericForeignKey.

Изменено в Django 3.2:

absolute_maxИ can_delete_extraбыли добавлены аргументы.

Общие отношения в админке

django.contrib.contenttypes.adminМодуль обеспечивает GenericTabularInlineи GenericStackedInline(подклассы GenericInlineModelAdmin)

Эти классы и функции позволяют использовать общие отношения в формах и в админке. Дополнительную информацию см. В документации по набору форм и администратору .

класс GenericInlineModelAdmin

GenericInlineModelAdmin Класс наследует все свойства от InlineModelAdminкласса. Однако он добавляет пару собственных для работы с родовым отношением:

ct_field

Имя поля ContentTypeвнешнего ключа в модели. По умолчанию content_type.

ct_fk_field

Имя целочисленного поля, представляющего идентификатор связанного объекта. По умолчанию object_id.

класс GenericTabularInline
класс GenericStackedInline

Подклассы GenericInlineModelAdminс составными и табличными макетами соответственно.

Copyright ©2021 All rights reserved