Django包括 contenttypes
可以跟踪Django供电项目中安装的所有模型的应用程序,为使用模型提供高级通用接口。
ContentTypes应用程序的核心是 ContentType
模型,住在 django.contrib.contenttypes.models.ContentType
. 实例 ContentType
表示和存储有关项目中安装的模型以及的新实例的信息 ContentType
在安装新模型时自动创建。
实例 ContentType
具有返回它们所表示的模型类以及从这些模型查询对象的方法。 ContentType
也有 custom manager 增加了使用的方法 ContentType
以及获取 ContentType
对于特定型号。
你的模型和 ContentType
还可以用于启用某个模型的实例与已安装的任何模型的实例之间的“通用”关系。
默认情况下包含ContentTypes框架 INSTALLED_APPS
创建的列表 django-admin startproject
,但如果您已将其移除或手动设置 INSTALLED_APPS
列表,您可以通过添加 'django.contrib.contenttypes'
对你 INSTALLED_APPS
设置。
安装ContentTypes框架通常是一个好主意;Django的其他捆绑应用程序中有几个需要它:
管理应用程序使用它来记录通过管理界面添加或更改的每个对象的历史记录。
贾戈 authentication framework
使用它将用户权限绑定到特定模型。
ContentType
模型¶的每个实例 ContentType
共有两个字段,它们共同唯一地描述已安装的模型:
模型所属应用程序的名称。这是从 app_label
模型的属性,并且仅包括 last 应用程序的python导入路径的一部分; django.contrib.contenttypes
例如,变为 app_label
属于 contenttypes
.
模型类的名称。
此外,以下属性可用:
内容类型的可读名称。这是从 verbose_name
模型的属性。
让我们看一个例子来看看这是如何工作的。如果你已经有了 contenttypes
已安装应用程序,然后添加 the sites application
对你 INSTALLED_APPS
设置与运行 manage.py migrate
要安装它,模型 django.contrib.sites.models.Site
将安装到数据库中。与之一起的是 ContentType
将使用以下值创建:
ContentType
实例¶各 ContentType
实例的方法允许您从 ContentType
它所代表的模型的实例,或从该模型检索对象:
获取一组有效的 lookup arguments 对于该模型, ContentType
代表,并且确实 a get() lookup
在该模型上,返回相应的对象。这个 using
参数可用于指定与默认数据库不同的数据库。
这个 using
添加了参数。
返回由此表示的模型类 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
¶ContentType
还有一个自定义管理器, ContentTypeManager
,其中添加了以下方法:
清除由使用的内部缓存 ContentType
跟踪为其创建的模型 ContentType
实例。您可能永远不需要自己调用这个方法;Django会在需要时自动调用它。
查找A ContentType
按ID。因为此方法使用的共享缓存与 get_for_model()
,这种方法比通常的方法更可取。 ContentType.objects.get(pk=id)
获取模型类或模型实例,并返回 ContentType
表示该模型的实例。 for_concrete_model=False
允许获取 ContentType
代理模型的。
获取模型类的可变数目,并返回将模型类映射到 ContentType
表示它们的实例。 for_concrete_models=False
允许获取 ContentType
代理模型。
返回 ContentType
由给定的应用程序标签和模型名称唯一标识的实例。此方法的主要目的是允许 ContentType
要通过引用的对象 natural key 在反序列化过程中。
这个 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
class Meta:
indexes = [
models.Index(fields=["content_type", "object_id"]),
]
正常的 ForeignKey
只能“指向”另一个模型,这意味着如果 TaggedItem
模型A ForeignKey
它必须选择一个而且只有一个模型来存储标签。ContentTypes应用程序提供了一个特殊的字段类型 (GenericForeignKey
)它围绕着这一点工作,并允许与任何模型的关系:
设置一个 GenericForeignKey
:
给你的模型A ForeignKey
到 ContentType
. 此字段的常用名称是“内容类型”。
为模型提供一个字段,该字段可以存储与之相关的模型中的主键值。对于大多数型号,这意味着 PositiveIntegerField
. 此字段的常用名称是“object_id”。
给你的模型A GenericForeignKey
,并将上述两个字段的名称传递给它。如果这些字段被命名为“content_type”和“object_id”,则可以省略这一点——它们是默认字段名。 GenericForeignKey
会寻找。
不同于 ForeignKey
,数据库索引是 not 在上自动创建 GenericForeignKey
,因此建议您使用 Meta.indexes
若要添加自己的多列索引,请执行以下操作。此行为 may change 在未来。
如果 False
,该字段将能够引用代理模型。默认是 True
. 这反映了 for_concrete_model
参数 get_for_model()
.
主键类型兼容性
“object_id”字段不必与相关模型上的主键字段类型相同,但它们的主键值必须通过其 get_db_prep_value()
方法。
例如,如果您希望允许与具有 IntegerField
或 CharField
主键字段,可以使用 CharField
因为整数可以被强制为字符串 get_db_prep_value()
.
为了获得最大的灵活性,您可以使用 TextField
它没有定义最大长度,但是这可能会导致严重的性能损失,具体取决于您的数据库后端。
没有一种适合所有字段类型的解决方案是最佳的。您应该评估期望指向的模型,并确定哪个解决方案对您的用例最有效。
将引用序列化到 ContentType
对象
如果正在序列化数据(例如,在生成 fixtures
)从实现通用关系的模型中,您可能应该使用自然键来唯一地标识相关的 ContentType
对象。见 natural keys 和 dumpdata --natural-foreign
更多信息。
这将启用一个类似于用于正常 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
不是普通的字段对象,这些示例将 not 工作:
# This will fail
>>> TaggedItem.objects.filter(content_object=guido)
# This will also fail
>>> TaggedItem.objects.get(content_object=guido)
同样地, GenericForeignKey
S不出现在 ModelForm
S
默认情况下,返回此对象的相关对象上的关系不存在。设置 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_name
设置允许从相关对象进行查询::
tags = GenericRelation(TaggedItem, related_query_name="bookmark")
这将启用筛选、排序和其他查询操作 Bookmark
从… 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
信号。
Django's database aggregation API 与 GenericRelation
。例如,您可以找出所有书签有多少标签:
>>> Bookmark.objects.aggregate(Count("tags"))
{'tags__count': 3}
这个 django.contrib.contenttypes.forms
模块提供:
返回A 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
)
这些类和函数允许在表单和管理中使用通用关系。见 model formset 和 admin 有关详细信息的文档。
这个 GenericInlineModelAdmin
类从继承所有属性 InlineModelAdmin
类。但是,它为处理一般关系添加了一些自己的内容:
的名字 ContentType
模型上的外键字段。默认为 content_type
.
表示相关对象ID的整数字段的名称。默认为 object_id
.
亚类 GenericInlineModelAdmin
分别使用堆叠和表格布局。
GenericPrefetch()
¶此查找类似于 Prefetch()
并且它应该只用于 GenericForeignKey
。这个 querysets
参数接受一组查询集,每个查询集对应一个不同的 ContentType
。这对以下方面很有用 GenericForeignKey
具有非齐次的结果集。
>>> 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>]>
7月 22, 2024