Инфраструктура contenttypes

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

Предварительный просмотр

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

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

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

Установка системы contenttypes

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

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

  • Приложение администрирования использует его для отслеживания истории каждого объекта, добавленного или измененного через интерфейс администрирования.
  • Django используется для разрешения ассоциированных пользователей к определенным моделям.système d'authentification

Модель ContentType

класс ContentType

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

app_label

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

model

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

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

name

Читаемое имя типа контента. Это происходит из атрибута verbose_name модели.

Давайте посмотрим на пример, чтобы увидеть, как это работает. Если приложение contenttypes уже установлено, то вы добавляете его sites в настройки INSTALLED_APPS и запускаете для его установки, шаблон будет установлен в вашей базе данных. В то же время будет создан новый экземпляр со следующими значениями:manage.py migrate django.contrib.sites.models.Site ContentType

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

Методы экземпляра ContentType

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

ContentType.get_object_for_this_type( ** kwargs )

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

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 используется шаблон с внешним ключом для ; это позволяет представлять такие понятия, как «может добавить запись в блог» или «может удалить новость».système de permissions Permission ContentType Permission

Менеджер 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_model=False позволяет получать экземпляры ContentType прокси-моделей.

get_by_natural_key( app_label , модель )

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

Этот метод get_for_model() особенно полезен, когда вы знаете, что с ним нужно работать, 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 используется ключ ForeignKey , ему придется выбрать один и только один шаблон для хранения связанных меток. Приложение contenttypes предоставляет специальный тип поля ( GenericForeignKey ), который решает эту проблему и позволяет установить связь с любой моделью:

класс GenericForeignKey

Чтобы настроить GenericForeignKey :

  1. Добавьте ключ ForeignKey к своей модели ContentType . Обычное имя для этого поля - content_type.
  2. Добавьте в модель поле, в котором могут храниться значения первичных ключей из моделей, которые будут предметом ссылки. Для большинства моделей это соответствует полю PositiveIntegerField . Обычное имя для этого поля - «object_id».
  3. Добавьте ключ в свою модель 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 базы данных. В качестве ключевого GenericForeignKey не является нормальным объектом поля, эти примеры не будут работать не  :

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

Точно так же ключи GenericForeignKey не появляются в формах ModelForm .

Обратные родовые отношения

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

Установив атрибут related_query_name из GenericRelation , это позволяет запрос от связанного объекта:

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 принятие имен полей типа контента и идентификатора объекта в качестве параметров 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 )

Возвращает один GenericInlineFormSet используя modelformset_factory() .

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

Параметр for_concrete_model соответствует параметру for_concrete_model из GenericForeignKey .

Общие отношения в интерфейсе администрирования

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

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

класс GenericInlineModelAdmin

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

ct_field

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

ct_fk_field

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

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

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

Copyright ©2020 All rights reserved