一. 什么是ContentTypes
Django ContentTypes是由Django框架提供的一个核心功能。Django ContentTypes是一个记录了项目中所有model元数据的表,表中一条记录对应着一个存在的model。
当使用django-admin初始化一个django项目的时候,可以看到在默认的INSTALL_APPS已经包含了django.contrib.contenttypes:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
django.contrib.contenttypes.models文件:
class ContentType(models.Model):
app_label = models.CharField(max_length=100)
model = models.CharField(_('python model class name'), max_length=100)
objects = ContentTypeManager()
class Meta:
verbose_name = _('content type')
verbose_name_plural = _('content types')
db_table = 'django_content_type'
unique_together = (('app_label', 'model'),)
def __str__(self):
return self.name
在第一次对Django的model进行migrate之后,就可以发现在数据库中出现了一张默认生成的名为django_content_type的表。 如果没有建立任何的model,默认django_content_type是这样的:
sqlite> select * from django_content_type;
1|admin|logentry
2|auth|group
3|auth|user
4|auth|permission
5|contenttypes|contenttype
6|sessions|session
django_content_type记录了当前的Django项目中所有model所属的app(即app_label属性)以及model的名字(即model属性)。 所以可以通过一个ContentType表的id和一个具体表中的id找到任何记录,即先通过ContenType表的id可以得到某个model,再通过model的id得到具体的对象。
二. ContentType实例的基本方法
1. ContentType.
get_object_for_this_type
(**kwargs)
为ContentType表示的模型获取一组有效的查找参数,并执行get()查找该模型,返回相应的对象。
2. ContentType.
model_class
()
返回由这个ContentType实例表示的模型类。
>>> from django.contrib.contenttypes.models import ContentType
>>> user_type = ContentType.objects.get(app_label="auth", model="user")
<ContentType: user>
>>>
>>> user_type.model_class()
<class 'django.contrib.auth.models.User'>
>>>
>>> user_type.get_object_for_this_type(username='Guido')
<User: Guido>
使用这些方法,您可以编写高级通用代码执行查询任何安装模型——而不是import和使用一个特定的模型类;再运行时,您可以传递app_label和model到一个ContentType的查询中,然后就可以使用查询结果中的model class或检索model中的对象。
-
您可以将另一个模型与ContentType关联起来,将它的实例与特定的模型类绑定在一起,并使用这些方法访问这些模型类。
三. 自定义管理器ContentTypeManager
1. get_for_id
(id)
通过id来查询ContentType
3. get_for_model
(model, for_concrete_model=True)
获取模型类或模型实例,并返回表示该模型的ContentType实例
4. get_for_models
(*models, for_concrete_models=True)
获取模型类的可变数量,并返回一个字典,该字典将模型类映射到表示它们的ContentType实例。
5. get_by_natural_key
(app_label, model)
返回由给定的应用程序标签和模型名称唯一标识的ContentType实例。
>>> ct=ContentType.objects.all()
>>> ct
<QuerySet [<ContentType: log entry>, <ContentType: permission>, <ContentType: group>, <ContentType: user>,
<ContentType: content type>, <ContentType: session>, <ContentType: blog>, <ContentType: blog type>, <ContentType: readnum>,
<ContentType: read num>, <ContentType: read detail num>]>
>>>
>>> ContentType.objects.get_for_id(1)
<ContentType: log entry>
>>>
>>> from blog.models import Blog
>>> from blog.models import BlogType
>>> ContentType.objects.get_for_model(Blog)
<ContentType: blog>
>>> ContentType.objects.get_for_models(Blog,BlogType)
{<class 'blog.models.Blog'>: <ContentType: blog>, <class 'blog.models.BlogType'>: <ContentType: blog type>}
>>> ContentType.objects.get_by_natural_key(app_label='blogstatistics',model='readnum')
<ContentType: read num>
四. Generic relations
从某个模型中添加外键到ContentType可以使您的模型有效地绑定到另一个模型类,从而使用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,那么它必须选择一个特定模型来存储Tag。contenttypes应用程序提供了一个特殊的字段类型(GenericForeignKey),它并允许与任何模型的关系。
一个标准的GenericForeignKey由三部分组成:
- 在model中定义ForeignKey字段,并关联到ContentType表。通常这个字段命名为“content_type”
- 在model中定义PositiveIntegerField字段,用来存储关联表中的主键。通常这个字段命名为“object_id”
- 在model中定义GenericForeignKey字段,传入上述两个字段的名字。
这将使API与普通的ForeignKey相似;每个TaggedItem都有一个content_object字段,该字段返回与之相关的对象,您也可以将其分配给该字段,或者在创建TaggedItem时使用:
五. Reverse generic relations
如果您知道您将最经常使用哪些模型,您还可以添加一个“反向”泛型关系来启用一个额外的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属性,可以用来检索它们关联的taggeditem:
>>> 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>]>