一、什么是Django ContentTypes?

  Django ContentTypes是由Django框架提供的一个核心功能,它对当前项目中所有基于Django驱动的model提供了更高层次的抽象接口。主要用来创建模型间的通用关系(generic relation)。

  进一步了解ContentTypes可以直接查阅以下这两个链接:

二、Django ContentTypes做了什么?

  当创建一个django项目时,可以看到在默认的INSTALL_APPS已经包含了django.contrib.contenttypes。

  1. # Application definition
  2.  
  3. INSTALLED_APPS = [
  4. 'django.contrib.admin',
  5. 'django.contrib.auth',
  6. 'django.contrib.contenttypes',
  7. 'django.contrib.sessions',
  8. 'django.contrib.messages',
  9. 'django.contrib.staticfiles',
  10. 'app01.apps.App01Config',
  11. ]

  注意:django.contrib.contenttypes是在django.contrib.auth之后,这是因为auth中的permission系统是根据contenttypes来实现的。

  导入contenttypes组件:

  1. from django.contrib.contenttypes.models import ContentType

  查看django.contrib.contenttypes.models.ContentType类的内容:

  1. class ContentType(models.Model):
  2. app_label = models.CharField(max_length=100)
  3. model = models.CharField(_('python model class name'), max_length=100)
  4. objects = ContentTypeManager()
  5.  
  6. class Meta:
  7. verbose_name = _('content type')
  8. verbose_name_plural = _('content types')
  9. db_table = 'django_content_type'
  10. unique_together = (('app_label', 'model'),)
  11.  
  12. def __str__(self):
  13. 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属性)。

  django_content_type并不只是记录属性这么简单.了contenttypes是对model的一次封装,因此可以通过contenttypes动态的访问model类型,而不需要每次import具体的model类型。

1、ContentType实例提供的接口

  • ContentType.model_class()

    获取当前ContentType类型所代表的模型类

  • ContentType.get_object_for_this_type()

    使用当前ContentType类型所代表的模型类做一次get查询

2、ContentType管理器(manager)提供的j接口

  • ContentType.objects.get_for_id()

    • 通过id寻找ContentType类型,这个跟传统的get方法的区别就是它跟get_for_model共享一个缓存,因此更为推荐。
  • ContentType.objects.get_for_model()
    • 通过model或者model的实例来寻找ContentType类型

三、Django ContentTypes框架使用场景

1、设计模型(创建表结构)

  假设我们创建如下模型,里面包含学位课程、专题课程、价格策略。

  价格策略既可以是专题课程的价格策略,也可以是学位课程的价格策略。需要在pricepolicy对象里添加非常多的ForeignKey。示例如下所示:

  1. class Food(models.Model):
  2. """
  3. id title
  4. 1 面包
  5. 2 牛奶
  6. """
  7. title = models.CharField(max_length=32)
  8. # 不会生成字段 只用于反向查询
  9. coupons = GenericRelation(to="Coupon")
  10.  
  11. class Fruit(models.Model):
  12. """
  13. id title
  14. 1 苹果
  15. 2 香蕉
  16. """
  17. title = models.CharField(max_length=32)
  18.  
  19. # 如果有40张表,则每一个都要建立外键关系
  20. class Coupon(models.Model):
  21. """
  22. id title food_id fruit_id
  23. 1 面包九五折 1 null
  24. 2 香蕉满10元减5元 null 2
  25. """
  26. title = models.CharField(max_length=32)
  27. food = models.ForeignKey(to="Food")
  28. fruit = models.ForeignKey(to="Fruit")

  这样做很傻,会造成代码重复和字段浪费。有一种优化的方案是:用两个字段去定位对象不用去创建多个外键关系。

  1. # 方法二:用两个字段去定位对象不用去创建多个外键关系
  2. class Coupon(models.Model):
  3. """
  4. id title table_id object_id(对应表对应对象的ID)
  5. 1 面包九五折 1 1
  6. 2 香蕉满10元减5元 2 2
  7. """
  8. title = models.CharField(max_length=32)
  9. table = models.ForeignKey(to="Table") # 与table表建立外键关系
  10. object_id = models.IntegerField() # 由object_id定位到表中的某一个对象,但没有建立外键关系
  11.  
  12. class Table(models.Model):
  13. """
  14. id app_name table_name
  15. 1 demo food
  16. 2 demo fruit
  17. """
  18. app_name = models.CharField(max_length=32)
  19. table_name = models.CharField(max_length=32)

  最好的方式是,只有当你需要对某个对象或模型进行评论时,才创建pricepolicy与那个模型的关系。示例如下所示:

  1. # 方法三:基于ContentTypes创建表结构
  2. class Coupon(models.Model):
  3. title = models.CharField(max_length=32) # 优惠券名称
  4. # 第一步:与ContentType表绑定外键关系
  5. content_type = models.ForeignKey(to=ContentType, on_delete=None)
  6. # 第二步:建立对象id
  7. object_id = models.IntegerField()
  8. # 第三步:content_type和object_id绑定外键关系
  9. content_object = GenericForeignKey("content_type", "object_id")

  学位课程、专题课程、价格策略基于django contenttypes创建表结构如下所示:

  1. from django.db import models
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
  4.  
  5. class DegreeCourse(models.Model):
  6. """学位课程"""
  7. name = models.CharField(max_length=128, unique=True)
  8. course_img = models.CharField(max_length=255, verbose_name="缩略图")
  9. brief = models.TextField(verbose_name="学位课程简介", )
  10.  
  11. class Course(models.Model):
  12. """专题课程"""
  13. name = models.CharField(max_length=128, unique=True)
  14. course_img = models.CharField(max_length=255)
  15.  
  16. # 不会在数据库生成列,只用于帮助你进行查询
  17. policy_list = GenericRelation("PricePolicy")
  18.  
  19. class PricePolicy(models.Model):
  20. """价格策略表"""
  21. content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # 关联course or degree_course
  22. object_id = models.PositiveIntegerField() # 正整数PositiveInteger
  23.  
  24. # GenericForeignKey不会在数据库生成列,只用于帮助你进行添加和查询
  25. content_object = GenericForeignKey('content_type', 'object_id') # 将两个字段放在这个对象中
  26.  
  27. # 周期
  28. valid_period_choices = (
  29. (1, '1天'),
  30. (3, '3天'),
  31. (7, '1周'), (14, '2周'),
  32. (30, '1个月'),
  33. (60, '2个月'),
  34. (90, '3个月'),
  35. (180, '6个月'), (210, '12个月'),
  36. (540, '18个月'), (720, '24个月'),
  37. )
  38. # 价格
  39. valid_period = models.SmallIntegerField(choices=valid_period_choices)
  40. price = models.FloatField()

(1)GenericForeignKey

  Django ContentType提供了一种GenericForeignKey的类型,通过这种类型可以指定content_object。

  GenericForeignKey不会在数据库生成列,只用于帮助你进行添加和查询。

(2)GenericRelation

  GenericRelation不会在数据库生成列,只用于帮助你进行查询。

(3)pricepolicy里有三个重要字段

  • content_type: 内容类型,代表了模型的名字(比如Course,DegreeCourse)

  • object_id: 传入对象的id

  • content_object: 传入的实例化对象,其包含两个属性content_type和object_id。

2、视图操作

(1)在价格策略表(pricepolicy)中添加数据

  1. from django.shortcuts import render, HttpResponse
  2. from app01 import models
  3. from django.contrib.contenttypes.models import ContentType
  4.  
  5. def test(request):
      # 方法一:
  6. models.PricePolicy.objects.create(
  7. valid_period=7,
  8. price=6.6,
  9. content_type=ContentType.objects.get(model="course"),
  10. object_id=1
  11. )
  12.   # 方法二:
  13. models.PricePolicy.objects.create(
  14. valid_period=14,
  15. price=9.9,
  16. content_object=models.Course.objects.get(id=1) # 'content_type', 'object_id'
  17. )
  18. return HttpResponse("...")

  访问http://127.0.0.1:8000/test/ 后,查看价格策略表保存的数据:

  

(2)根据某个价格策略对象,找到其对应的表和数据

  这里以查看管理课程名称为例:

  1. from django.shortcuts import render, HttpResponse
  2. from app01 import models
  3. from django.contrib.contenttypes.models import ContentType
  4.  
  5. def test(request):
  6. price = models.PricePolicy.objects.get(id=2)
  7. print(price.content_object.name) # 21天入门python 即自动帮忙找到对应的对象
  8.  
  9. return HttpResponse("...")

(3)找到某个课程关联的所有价格策略

  注意这里需要利用到GenericRelation。

  1. from django.shortcuts import render, HttpResponse
  2. from app01 import models
  3. from django.contrib.contenttypes.models import ContentType
  4.  
  5. def test(request):
  6. obj = models.Course.objects.get(id=1)
  7. print(obj.policy_list.all()) # <QuerySet [<PricePolicy: PricePolicy object (1)>, <PricePolicy: PricePolicy object (2)>]>
  8.  
  9. return HttpResponse("...")

  查询结果是一个QuerySet对象,如果想让查询结果更加清楚:

  1. from django.shortcuts import render, HttpResponse
  2. from app01 import models
  3. from django.contrib.contenttypes.models import ContentType
  4.  
  5. def test(request):
  6.  
  7. obj = models.Course.objects.get(id=1)
  8. for item in obj.policy_list.all():
  9. print(item.id, item.valid_period, item.price)
  10. """
  11. 1 7 6.6
  12. 2 14 9.9
  13. """
  14. return HttpResponse("...")

四、总结ContentType

  如果一张表与N张表动态地要创建Foreign Key关系,如果创建 Foreign key 将生成很多列,这样很多都是空的,造成严重浪费空间。只要是一张表要和多张表建立外键关系的情况,都可以考虑使用django的ContentType组件来帮助实现,以简化表结构的设计。

  ContentType组件的作用:可以通过两个字段(GenericForeignKey, GenericRelation),在保证列数不变的情况下,让一张表和N张表做Foreign Key关系。

django内置组件——ContentTypes的更多相关文章

  1. Django:内置组件Content-Type

    12.Django组件之Content_Type 1.帮助我们生成了一张表,里面有所有表名.这样不再自建表在表中填表名,用Foreignkey获取 2.为了让我们快速进入插入数据,填写一个字段Gene ...

  2. Django内置Admin

    Django内置的Admin是对于model中对应的数据表进行增删改查提供的组件,使用方式有: 依赖APP: django.contrib.auth django.contrib.contenttyp ...

  3. 框架----Django内置Admin

    Django内置的Admin是对于model中对应的数据表进行增删改查提供的组件,使用方式有: 依赖APP: django.contrib.auth django.contrib.contenttyp ...

  4. Django内置Admin解析

    Django 内置的admin是对于model中对应的数据表进行增删改查提供的组建 一.Django admin的内部依赖: 依赖的app django.contrib.auth django.con ...

  5. Django学习——ajax发送其他请求、上传文件(ajax和form两种方式)、ajax上传json格式、 Django内置序列化(了解)、分页器的使用

    1 ajax发送其他请求 1 写在form表单 submit和button会触发提交 <form action=""> </form> 注释 2 使用inp ...

  6. Vue基础(环境配置、内部指令、全局API、选项、内置组件)

    1.环境配置 安装VsCode 安装包管理工具:直接下载 NodeJS 进行安装即可,NodeJS自带 Npm 包管理工具,下载地址:https://nodejs.org/en/download/安装 ...

  7. Django内置分页

    一.django内置分页 from django.shortcuts import render from django.core.paginator import Paginator, EmptyP ...

  8. Django 内置分页器

    先导入Django内置的分页器 在商品列表页或者购物车列表页导入 在渲染list.html导入 然后在views后台渲染方法写入 打开list页面结果

  9. Django内置权限扩展案例

    当Django的内置权限无法满足需求的时候就自己扩展吧~ 背景介绍 overmind项目使用了Django内置的权限系统,Django内置权限系统基于model层做控制,新的model创建后会默认新建 ...

随机推荐

  1. SpringBoot Maven多模块整合MyBatis 打包jar

    最近公司开始新的项目,框架选定为SpringBoot+Mybatis,本篇主要记录了在IDEA中搭建SpringBoot多模块项目的过程. 源码:https://github.com/12641561 ...

  2. 老男孩Day15作业:商城列表页面(静态)

    一. 一.作业需求: 1.完成商城列表静态页面的抒写 二.博客地址:https://www.cnblogs.com/catepython/p/9205636.html 三.运行环境 操作系统:Win1 ...

  3. 修改两行代码,让nginx支持phpinfo模式

    Nginx服务器默认不支持pathinfo, 在需要pathinfo支持的程序中(如thinkphp),则无法支持”/index.php/Home/Index/index”这种网址. 网上流传的解决办 ...

  4. flask 发送QQ邮箱

    from flask import Flask from flask_script import Manager, Shell from flask_mail import Mail, Message ...

  5. n阶幻方

    前序 最近在学习一些经典的算法,搞得头昏脑涨,就想换换脑子.在家里的旧书堆里面乱翻,无意中将一本具有十多年历史的小学数学奥林匹克竞赛的书发掘了出来,能放到现在挺不容易的,就拿起来随便翻翻.看了看目录, ...

  6. kotlin 注意的地方

    1 . kotlin let 用法: let(val -> ) 注意:这  -> 后面不能有 花括号!!!! 2 . kotlin 中 如果使用了 @Transactional 注解.请让 ...

  7. 1017 A除以B (20 分)

    #include <iostream> #include <string> using namespace std; int main() { string num; int ...

  8. mfix的Negative gas density报错解决

    错误很难定位,因为编译正常,而是运行过程中会告知出现Negative gas density,并且不收敛,没有其他错误信息.最后通过一步步定位发现是由于 IC_EP_g(1) < EP_star ...

  9. leetcode 75 Sort Colors 计数排序,三路快排

    解法一:计数排序:统计0,1,2 的个数 时间复杂度:O(n) 空间复杂度:O(k)    k为元素的取值范围, 此题为O(1) class Solution { public: void sortC ...

  10. drf之序列化器的使用

    一.序列化器-Serializer 作用: 1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串 2. 完成数据校验功能 3. 反序列化,把客户端发送过来的数据,经 ...