Django ContentTypes是由Django框架提供的一个核心功能,它对当前项目中全部基于Django驱动的model提供了更高层次的抽象接口。主要用来建立模型间的通用关系(generic relation)。python
进一步了解ContentTypes能够直接查阅如下这两个连接:数据库
当建立一个django项目时,能够看到在默认的INSTALL_APPS已经包含了django.contrib.contenttypes。django
# Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', ]
注意:django.contrib.contenttypes是在django.contrib.auth以后,这是由于auth中的permission系统是根据contenttypes来实现的。缓存
导入contenttypes组件:session
from django.contrib.contenttypes.models import ContentType
查看django.contrib.contenttypes.models.ContentType类的内容:app
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
能够看到ContentType就是一个简单的django model,并且它在数据库中的表的名字为django_content_type。 框架
在第一次对Django的model进行migrate以后,就能够发如今数据库中出现了一张默认生成的名为django_content_type的表。
若是没有创建任何的model,默认django_content_type是前六项:优化
django_content_type记录了当前的Django项目中全部model所属的app(即app_label属性)以及model的名字(即model属性)。 ui
django_content_type并不仅是记录属性这么简单.了contenttypes是对model的一次封装,所以能够经过contenttypes动态的访问model类型,而不须要每次import具体的model类型。this
获取当前ContentType类型所表明的模型类
使用当前ContentType类型所表明的模型类作一次get查询
假设咱们建立以下模型,里面包含学位课程、专题课程、价格策略。
价格策略既能够是专题课程的价格策略,也能够是学位课程的价格策略。须要在pricepolicy对象里添加很是多的ForeignKey。示例以下所示:
class Food(models.Model): """ id title 1 面包 2 牛奶 """ title = models.CharField(max_length=32) # 不会生成字段 只用于反向查询 coupons = GenericRelation(to="Coupon") class Fruit(models.Model): """ id title 1 苹果 2 香蕉 """ title = models.CharField(max_length=32) # 若是有40张表,则每个都要创建外键关系 class Coupon(models.Model): """ id title food_id fruit_id 1 面包九五折 1 null 2 香蕉满10元减5元 null 2 """ title = models.CharField(max_length=32) food = models.ForeignKey(to="Food") fruit = models.ForeignKey(to="Fruit")
这样作很傻,会形成代码重复和字段浪费。有一种优化的方案是:用两个字段去定位对象不用去建立多个外键关系。
# 方法二:用两个字段去定位对象不用去建立多个外键关系 class Coupon(models.Model): """ id title table_id object_id(对应表对应对象的ID) 1 面包九五折 1 1 2 香蕉满10元减5元 2 2 """ title = models.CharField(max_length=32) table = models.ForeignKey(to="Table") # 与table表创建外键关系 object_id = models.IntegerField() # 由object_id定位到表中的某一个对象,但没有创建外键关系 class Table(models.Model): """ id app_name table_name 1 demo food 2 demo fruit """ app_name = models.CharField(max_length=32) table_name = models.CharField(max_length=32)
最好的方式是,只有当你须要对某个对象或模型进行评论时,才建立pricepolicy与那个模型的关系。示例以下所示:
# 方法三:基于ContentTypes建立表结构 class Coupon(models.Model): title = models.CharField(max_length=32) # 优惠券名称 # 第一步:与ContentType表绑定外键关系 content_type = models.ForeignKey(to=ContentType, on_delete=None) # 第二步:创建对象id object_id = models.IntegerField() # 第三步:content_type和object_id绑定外键关系 content_object = GenericForeignKey("content_type", "object_id")
学位课程、专题课程、价格策略基于django contenttypes建立表结构以下所示:
from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation class DegreeCourse(models.Model): """学位课程""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255, verbose_name="缩略图") brief = models.TextField(verbose_name="学位课程简介", ) class Course(models.Model): """专题课程""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255) # 不会在数据库生成列,只用于帮助你进行查询 policy_list = GenericRelation("PricePolicy") class PricePolicy(models.Model): """价格策略表""" content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # 关联course or degree_course object_id = models.PositiveIntegerField() # 正整数PositiveInteger # GenericForeignKey不会在数据库生成列,只用于帮助你进行添加和查询 content_object = GenericForeignKey('content_type', 'object_id') # 将两个字段放在这个对象中 # 周期 valid_period_choices = ( (1, '1天'), (3, '3天'), (7, '1周'), (14, '2周'), (30, '1个月'), (60, '2个月'), (90, '3个月'), (180, '6个月'), (210, '12个月'), (540, '18个月'), (720, '24个月'), ) # 价格 valid_period = models.SmallIntegerField(choices=valid_period_choices) price = models.FloatField()
Django ContentType提供了一种GenericForeignKey的类型,经过这种类型能够指定content_object。
GenericForeignKey不会在数据库生成列,只用于帮助你进行添加和查询。
GenericRelation不会在数据库生成列,只用于帮助你进行查询。
content_type: 内容类型,表明了模型的名字(好比Course,DegreeCourse)
object_id: 传入对象的id
content_object: 传入的实例化对象,其包含两个属性content_type和object_id。
from django.shortcuts import render, HttpResponse from app01 import models from django.contrib.contenttypes.models import ContentType def test(request):
# 方法一: models.PricePolicy.objects.create( valid_period=7, price=6.6, content_type=ContentType.objects.get(model="course"), object_id=1 ) # 方法二: models.PricePolicy.objects.create( valid_period=14, price=9.9, content_object=models.Course.objects.get(id=1) # 'content_type', 'object_id' ) return HttpResponse("...")
访问http://127.0.0.1:8000/test/ 后,查看价格策略表保存的数据:
这里以查看管理课程名称为例:
from django.shortcuts import render, HttpResponse from app01 import models from django.contrib.contenttypes.models import ContentType def test(request): price = models.PricePolicy.objects.get(id=2) print(price.content_object.name) # 21天入门python 即自动帮忙找到对应的对象 return HttpResponse("...")
注意这里须要利用到GenericRelation。
from django.shortcuts import render, HttpResponse from app01 import models from django.contrib.contenttypes.models import ContentType def test(request): obj = models.Course.objects.get(id=1) print(obj.policy_list.all()) # <QuerySet [<PricePolicy: PricePolicy object (1)>, <PricePolicy: PricePolicy object (2)>]> return HttpResponse("...")
查询结果是一个QuerySet对象,若是想让查询结果更加清楚:
from django.shortcuts import render, HttpResponse from app01 import models from django.contrib.contenttypes.models import ContentType def test(request): obj = models.Course.objects.get(id=1) for item in obj.policy_list.all(): print(item.id, item.valid_period, item.price) """ 1 7 6.6 2 14 9.9 """ return HttpResponse("...")
若是一张表与N张表动态地要建立Foreign Key关系,若是建立 Foreign key 将生成不少列,这样不少都是空的,形成严重浪费空间。只要是一张表要和多张表创建外键关系的状况,均可以考虑使用django的ContentType组件来帮助实现,以简化表结构的设计。
ContentType组件的做用:能够经过两个字段(GenericForeignKey, GenericRelation),在保证列数不变的状况下,让一张表和N张表作Foreign Key关系。