【Django】--Contenttypes框架,了解内容类型和范型关系
Django中文文档1.8 https://legacy.gitbook.com/book/wizardforcel/django-chinese-docs-18/details
Contenttypes
- Django的contenttypes应用,可以追踪安装在Django项目里的所有应用,并提供一个高层次及通用的接口用于与你的模型进行交互。
- Contenttypes的核心应用是ContentType模型,位置:django.contrib.contenttypes.models.ContentType
- 通过添加 'django.contrib.contenttypes' 到你的 INSTALLED_APPS 设置中来启用它。
Contenttypes相关方法
1.class ContentType[source]
每一个 ContentType 实例有两个字段,共同来唯一描述一个已经安装的模型,分别是:
app_label
模型所在的应用的名称。 这取自模型的 app_label 属性,并只包括应用的Python 导入路径的最后的部分。
例如,"django.contrib.contenttypes"的 app_label 是"contenttypes".
model
模型的类的名称
额外属性:
name[source]
Contenttype 的人类可读的的名称。它取之于模型的 verbose_name 属性.
Changed in Django 1.8:
安装 contenttypes 应用后,添加 sites应用 到你的 INSTALLED_APPS 设置并运行 manage.py migrate 来安装它,模型 django.contrib.sites.models.Site 将安装到你的数据库中。
同时将创建 ContentType 的一个具有以下值的新实例︰
app_label 将设置为 'sites' (Python 路径"django.contrib.sites"的最后部分)。
model 将设置为 'site' 。
2.`get_object_for_this_type (**kwargs)[source]
接收 ContentType 表示的模型所接收的查询参数,对该模型做 一次get() 查询 ,然后返回相应的对象
3.model_class ()[source]
返回此 ContentType 实例所表示的模型类。
例如,我们可以查找 User 模型的 ContentType ︰
>>> 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 作为一种绑定它到特定模型类的方式,然后使用这些方法来获取对那些模型的访问。
4.ContentTypeManager
class ContentTypeManager [source]
ContentType 还具有自定义的管理器 ContentTypeManager ,它增加了下列方
1.get_for_id (id)
代替:ContentType.objects.get(pk=id)
原因:此方法使用与 get_for_model() 有相同的共享缓存
2.get_for_model(model[, for_concrete_model=True])
接收一个模型类或模型的实例,并返回表示该模型的 ContentType 实例。 for_concrete_model=False 允许获取代理模型的 ContentType 。
3.get_for_models (*models[, for_concrete_models=True])
接收可变数目的模型类,并返回一个字典,将模型类映射到表示它们的 ContentType 实例。 for_concrete_model=False 允许获取代理模型的 ContentType 。
4.clear_cache ()[source]
清除 ContentType 用于跟踪模型的内部缓存,它已为其创建 ContentType 实例。你可能不需要自己调用此方法;Django 将在它需要的时候自动调用。
5.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>
通用关系
在你的model添加一个外键到 ContentType 这将允许你更快捷的绑定自身到其他的model class,就像上述的 Permission model 一样。
但是它非常有可能进一步的利用 ContentType 来实现真正的 generic (有时称之为多态) relationships 在models之间。
一个简单的例子是标记系统,它可能看起来像这样:
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 只能指向其他任意一个model,这是因为TaggedItem model用一个 ForeignKey只能关联一个model,也只能有一个model存储tags.
contenttypes application 提供了一个特殊的字段(GenericForeignKey)避免了这个问题,并且允许你和任何一个model建立关联关系
class GenericForeignKey[source]
三步建立GenericForeignKey:
1.给你的model设置一个ForeignKey字段到ContentType.一般命名为:“content_type”.
2.给你的model设置一个字段,用来存储你想要关联的model主键值。对于大多数model,这是一个 PositiveIntegerField字段。并且通常命名为“object_id”.
3.给你的model一个 GenericForeignKey字段,把1,2点提到的那两个字段的名词传给他。如果这两个字段名字分别为“content_type”和“object_id”,你就可以省略他们。因为GenericForeignKey默认会去自动查找这两个字段。
for_concrete_model
默认是 True .
如果为 False ,那么字段将会涉及到proxy models(代理模型)。
这映射了 for_concrete_model 的参数到 get_for_model() .
allow_unsaved_instance_assignme
New in Django 1.8.
与 ForeignKey.allow_unsaved_instance_assignment 类似。
自1.7版起已弃用:
此类过去在 django.contrib.contenttypes.generic 中定
义。将从Django 1.9中删除从此旧位置导入的支持。
主键类型的兼容性
“object_id” 字段并不总是相同的,这是由于储存在相关模型中的主键类型的关系,但
是他们的主键值必须被转变为相同类型。
通过 “object_id” 字段 的get_db_prep_value() 方法实现。
例如, 如果你想要简历generic 关系到一个 IntegerField 或者 CharField 为主
键的模型, 你可以使用 CharField 给 “object_id”字段,因为数字是可以被
get_db_prep_value() 转化为字母的
序列化 ContentType 的对象引用
如果你想要序列化一个建立了 generic关系的model数据(for example, whengenerating fixtures ) ,你应该用一个自然键来唯一的标识相关的ContentType 对象。有关详细信息,请参阅natural keys和 dumpdata --natural-foreign 。
这允许你像平时用 ForeignKey 类似的API来工作。每一个 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 完成的方式问题,,你没有办法用这个字段直接执行数据库API,filters的操作。
比如 ( filter() and exclude() , for example) 。因为一个 GenericForeignKey 不是一个普通的字段对象t, 这些例子是不会工作的:
# This will fail >>> TaggedItem.objects.filter(content_object=guido) # This will also fail >>> TaggedItem.objects.get(content_object=guido)
同样的, GenericForeignKey s 是不会出现在 ModelForm S
反向通用关系
class GenericRelation [source]
自1.7版起已弃用:此类过去在 django.contrib.contenttypes.generic 中定义。
将从Django 1.9中删除从此旧位置导入的支持
related_query_name
New in Django 1.7
默认情况下,相关对象返回到该对象的关系不存在。设置 related_query_name来创建一个对象从关联对象返回到对象自身。这允许查询和筛选相关的对象。
如果你知道你最经常使用哪种型号的,你还可以添加一个“反向”的通用关系,以使其能附加一个附加的API。
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>]
New in Django 1.7
定义一个 GenericRelation 伴有 related_query_name 可以允许从相关联的对象中查询。
tags = GenericRelation(TaggedItem, related_query_name='bookmarks’)
这允许你从 TaggedItem 执行过滤筛选, 排序, 和其他的查询操作 Bookmark
>>> # Get all tags belonging to books containing `django` in the url
>>> TaggedItem.objects.filter(bookmarks__url__contains='django')
[<TaggedItem: django>, <TaggedItem: python>]
GenericRelation和GenericForeignKey一样可以接受以content-type和object-id字段命名的参数。
如果一个model的GenericForeignKey字段使用的不是默认的命名,当你创建一个GenericRelation的时候一定要显示的传递这个字段命名给它。例如:
TaggedItem model关联到上述所用的字段用content_type_fk 和 object_primary_key两个名称来创建一个generic foreignkey,然后就需要这样定义:
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 model在它的GenericForeignKey的ct_field or fk_field 使用了非默认值,例如:
ct_field = "object_pk" , 你就要设置GenericRelation中的ct_field和fk_field:
comments = fields.GenericRelation(Comment, object_id_field="object_pk")
同时请注意,如果你删除了一个具有 GenericRelation 的对象, 任何以GenericForeignKey 指向他的对象也会被删除. 在上面的例子中, 如果一个Bookmark 对象被删除了,任何指向它的 TaggedItem 对象也会被同时删除.
不同于 ForeignKey , GenericForeignKey 并不接受一个 on_delete 参数来控制它的行为:如果你非常渴望这种可控制行为,你应该不使用GenericRelation 来避免这种级联删除,并且这种行为控制也可以通过pre_delete 信号来提供。
通用关系和聚合
Django’s database aggregation API不能与 GenericRelation 配合使用。例如,您可能会试图尝试以下操作:
Bookmark.objects.aggregate(Count('tags'))
这是错误的。然而generic relation 添加了额外的查询集过滤来保证正确的内容类型,但是aggregate()方法并没有被考虑进来。现在,如果你需要再generic relations 使用聚合,你只能不通过聚合API来计算他们。
Generic relation在表单中的应用
django.contrib.contenttypes.forms 模块提供:
BaseGenericlnlineFormSet
用于GenericForeignKey的表单工厂,generic_inlineformaset_factory().
class BaseGenericInlineFormSet [source]
自1.7版起已弃用:此类过去在 django.contrib.contenttypes.generic 中定义。将从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)
使用 modelformset_factory() 返回 GenericInlineFormSet.
如果它们分别与默认值, content_type 和 object_id 不同,则必须提供 ct_field 和 fk_field 。
其他参数与 modelformset_factory() 和 inlineformset_factory() 中记录的参数类似.
for_concrete_model 参数对应于 GenericForeignKey 上的 for_concrete_model 参数。
自1.7版起已弃用:此函数用于在 django.contrib.contenttypes.generic 中定义。
将从Django 1.9中删除从此旧位置导入的支持。
Changed in Django 1.7:
min_num 和 validate_min.
管理中的通用关系
django.contrib.contenttypes.admin 模块提供GenericTabularInlineGenericStackedInline(GenericInlineModel的子类别)
这些类和函数确保了generic relations在forms 和 admin的使用。有关详细信息,请参阅model formset和admin文档。
class GenericInlineModelAdmin [source]
GenericInlineModelAdmin 类继承了来自 InlineModelAdmin 类的所有属性。但是,它添加了一些自己的用于处理通用关系:
ct_field
模型上的ContentType外键字段的名称.默认为content_type。
ct_fk_field
表示相关对象的ID的整数字段的名称。默认为object_id。
自1.7版起已弃用:此类过去在 django.contrib.contenttypes.generic 中定义。将从Django 1.9中删除从此旧位置导入的支持。
class GenericTabularInline [source]
class GenericStackedInline [source]
GenericInlineModelAdmin 的子类,分别具有堆叠和表格布局.
自1.7版起已弃用:这些类以前在 django.contrib.contenttypes.generic 中定义。将从Django 1.9中删除从此旧位置导入的支持.