一. 什么是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>]>