Django ContentType 的使用
引入
一切优化,最终都是关于需求的优化。本文介绍需求确定之后的数据库表结构设计优化。
程序员应该都知道,编程是数据结构和算法的结合。所谓数据就是用户需要访问和操作的资源,比如购物类App里面的商品,图书、衣服、鞋帽等等。算法就是我们通过一系列的获取数据、过滤数据、汇总并编排数据并最终展现给用户的一个过程。
算法的实现复杂度非常重要,因为它直接关乎到用户体验,算法实现简单则用户体验好,反则用户体验差,而算法的实现复杂度直接与数据的存储结构相关,数据的存储结构如果设计的非常好,那么向用户展示数据的算法可能会非常简单,反之,则将异常复杂。
因此,数据结构或者说,数据库表结构的设计是至关重要的。
现在的应用程序,持久存储数据采用的是关系数据库,即Oracle,MySQL等等数据库软件,而用户需要的数据,则是存储到数据库表中,不同的人,设计出来的表结构不同,导致算法的实现过程会有非常明显的差别。
概念
Django有一个用来跟踪所以已安装App的 models 的框架,名为 contenttypes 。
ContentType其实是Django原生App之一,也是 contenttypes 的实现基础。
该应用提供了一种高级的、通用的接口用来管理和维护我们应用程序的 models 。
拿之前的DRF项目来举例,该框架会为每一个APP的 models 创建对应的信息,该信息存储在 content_type 表中,请看下图:
如上图所示,每一个App对应多个 model , 存储在名为 django_content_type 的表中,该表只有 id , app_label 和 model 三个字段。第一个字段存储的是 model 的序号,下文中, contenttypes 框架正是通过 model 的 id 找到该 model , app_label 存储的是App的名称,而 model 字段则存储的是每一个 model 的名称。
安装使用
使用 django-admin startproject 命令创建一个Django项目后,在 INSTALLED_APPS 列表中会出现该框架,如下图所示:
什么时候需要用到该框架
如果查看官方文档,你只会发现一些官方的对于该框架的解释,但是在什么地方能够发挥它的最大价值,官方文档中并没有详细说明,接下来,设计一个需求,并根据这个需求的优化,来一步步学习 contenttypes 框架。
一个简单的需求
假设现在需要开发一个课程App,该app共有如下课程表:
共四个课程表:操作系统基础,Python基础,面向对象,Web框架。
需求
学生学完每个课程的最后一门课后,会获得该门课程的优惠券和该课程的总优惠券,比如学生学完操作系统原理课程后,会获得操作系统原理通关优惠券和操作系统基础课程优惠券两张优惠券。
第一次优化
看到如此多的优惠券表,作为程序员,这不能忍,要好好优化优化,不然,这就是给自己挖了一个巨大的坑,以后扩展和维护肯定会特别不方便。如何优化呢?
是否可以将如此多的优惠券表合成一张表呢?
这样就实现了使用一张优惠券表来存储所有的优惠券和对应的课程的关系信息(使用了课程id),注意每个课程id字典都是该优惠券表的一个外键字段。
第二次优化
经过第一次优化,表数量减少了,但是对这样的表进行增删改查操作将会非常麻烦,经过优化,对这个表的增删改查操作速度有了非常明显的提升。
仔细想想,设计优惠券和课程之间的关系,无非就是为了增删改查这些操作,只要能够通过课程id找到它所对应的所有优惠券,或者通过优惠券能唯一定位到某一个具体的课程,这就是本质需求。
现在的问题是,课程 id 是重复的,每一个表里面的 id 都是从1开始计数,而优惠券里面的 id 如果仅仅只是存储课程 id ,很显然,就无法唯一定位到具体的课程。那么,通过什么方式来唯一定位 id 呢?
请看下图的优化:
看到了吗?使用的 table_id 和 table_row_id 来唯一定义某个课程表中的某一个课程,首先,必须通过某一个信息定位到具体的表,然后再通过课程id来定位具体的课程。
那么, table_id 从哪里来呢?
上面的 django_content_type 表吗?它里面有三个字段,其中id字段存储的就是整个Django项目中所有表的序号id。
优惠券表共有四个字段, id 字段, coupon_name 字段存储优惠券信息, table_id 字段存储的是课程表的 id ,它应该是一个外键,关联到 django_content_type 表, table_row_id 字段,该字段的性质也是外键,但是不能具体指向某一个表,因为,该字段存储的是所有表的 id 。
优化到这里,差不多了,算是比较合适的表结构设计了。但是看起来还有点麻烦,特别是 table_row_id 字段。不是特别好实现,下面就到 contenttypes 的使用了。
使用contenttypes建表
1.导入模块
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
2.建立模型类
class OperationSystem(models.Model):
"""
id course_name
1 "计算机基础"
2 "计算机组成原理"
3 "操作系统原理"
"""
course_name = models.CharField(max_length=32)
coupons = GenericRelation(to="Coupon") def __str__(self):
return self.course_name class PythonBasic(models.Model):
"""
id course_name
1 "数据类型"
2 "字符编码"
3 "文件操作"
"""
course_name = models.CharField(max_length=32)
coupons = GenericRelation(to="Coupon") def __str__(self):
return self.course_name class Oop(models.Model):
"""
id course_name
1 "面向对象三大特性"
2 "元类"
"""
course_name = models.CharField(max_length=32)
coupons = GenericRelation(to="Coupon") def __str__(self):
return self.course_name class WebFramework(models.Model):
"""
id course_name
1 "web框架原理"
2 "ORM"
"""
course_name = models.CharField(max_length=32)
coupons = GenericRelation(to="Coupon") def __str__(self):
return self.course_name class Coupon(models.Model):
"""
id name content_type_id object_id
1 "操作系统优惠券" 9 2
2 "Python 优惠券" 9 2
"""
coupon_name = models.CharField(max_length=32)
content_type = models.ForeignKey(ContentType, verbose_name="关联到django的ContentType表", on_delete=models.CASCADE)
object_id = models.PositiveIntegerField(verbose_name="关联表中的数据行ID")
content_object = GenericForeignKey("content_type", "object_id") def __str__(self):
return self.coupon_name
使用方式:
- 导入模块一个是 ContentType 这个 model
- 另一个是 GenericForeignKey 和 GenericRelation
优惠券表包含 coupon_name , 该字段是一个外键,关联到 ContentType 表;
还包含 object_id 字段, 该字段存储课程id;
这两个字段的默认名称分别为 content_type , object_id ;
另外最重要的是 content_object 字段,它是 GenericForeignKey 这个类的实例化对象,创建这个对象时,需要把上面两个字段作为参数传递给它。
对于优惠券表的所有数据操作,只要是涉及到需要查找优惠券对应的课程,或者通过课程查找其对应的优惠券,都是通过 content_object 来进行的。
值得注意的是,该字段并不真实存在表中, Coupon 表结构如下图所示:
基本数据操作
先插入一些基础数据,也就是第一张图中展示的数据。
操作ContentType
上文提到, ContentType 是一个表,这个表提供一些方法以便进行数据操作:
>>> from django.contrib.contenttypes.models import ContentType >>> course_type = ContentType.objects.get(app_label="course", model="oop") >>> course_type
Out[7]: <ContentType: oop> # 获取类名:字符串形式
>>> course_type.model
Out[8]: 'oop' # 获取model,之后可以通过objects.all来查询数据
>>> course_type.model_class()
Out[9]: course.models.Oop # 查询数据
>>> course_type.get_object_for_this_type(course_name="元类")
Out[10]: <Oop: 元类>
给课程字符编码添加一个字符编码通关优惠券:
添加数据
>>> from course.models import PythonBasic >>> pb_obj = PythonBasic.objects.get(id=2) >>> from course.models import Coupon >>> Coupon.objects.create(coupon_name="字符编码通关优惠券", content_object=pb_obj)
Out[15]: <Coupon: 字符编码通关优惠券>
查看数据是否添加成功:
可以看到,是第九张表的第二行数据,查看 django_content_type 表发现,该课程所在的表 Python 基础表,在整个项目中的确是第九张表,而且该课程在该表中是第二行数据, id 为2。
这样,就可以通过表的 id 和数据 id 具体定位到某一个课程,然后给该课程绑定一个优惠券。当然还可以给它绑定多个优惠券。
>>> from course.models import Coupon
>>> from course.models import PythonBasic
>>> pb_obj = PythonBasic.objects.get(id=2)
>>> Coupon.objects.create(coupon_name="字符编码通关优惠券", content_object=pb_obj)
可以看到,定位具体的课程信息是通过 content_object 这个并不存在 Coupon 表中的字段来操作的。
删除数据
删除字符编码通关优惠券对应的所有课程
>>> Coupon.objects.filter(coupon_name="字符编码通关优惠券").delete()
或者
>>> pb_obj = PythonBasic.objects.get(id=2)
>>> ob_obj.coupons.all().delete()
修改数据
>>> Coupon.objects.filter(coupon_name="字符编码通关优惠券").update(coupon_name="编码通关优惠券")
查询数据
查询面向对象通关优惠券绑定了那些课程
>>> coupon_obj = Coupon.objects.filter(coupon_name="面向对象通关优惠券").first()
>>> coupon_obj.content_type # <PythonBasic: 字符编码>
使用反向查询字段查看字符编码课程共有哪些优惠券
>>> pb_obj = PythonBasic.objects.get(id=2)
>>> pb_obj.coupons.all()
Out[35]: <QuerySet [<Coupon: 字符编码通关优惠券>, <Coupon: 面向对象通关优惠券>]>
~>.<~
Django ContentType 的使用的更多相关文章
- python 全栈开发,Day98(路飞学城背景,django ContentType组件,表结构讲解)
昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...
- Django——ContentType及ContentType-signals的使用
一.ContentType 在django中,有一个记录了项目中所有model元数据的表,就是ContentType,表中一条记录对应着一个存在的model,所以可以通过一个ContentType表的 ...
- Django ContentType组件
ContentType组件 引入 现在我们有这样一个需求~我们的商城里有很多的商品~~节日要来了~我们要搞活动~~ 那么我们就要设计优惠券~~优惠券都有什么类型呢~~满减的~折扣的~立减的~~ 我们对 ...
- Django content-type 使用
1.models class PricePolicy(models.Model): """价格与有课程效期表""" content_type ...
- day70 csrf简单用法 &Django ContentType
一. 什么是跨站请求伪造 CSRF def transfer(request): if request.method =='POST': from_ =request.POST.get('from') ...
- Django——ContentType(与多个表建立外键关系)及ContentType-signals的使用
一.ContentType 在django中,有一个记录了项目中所有model元数据的表,就是ContentType,表中一条记录对应着一个存在的model,所以可以通过一个ContentType表的 ...
- django contenttype 表应用
Django contenttypes 应用 contenttypes 是Django内置的一个应用,可以追踪项目中所有app和model的对应关系,并记录在ContentType表中. 每当我们创建 ...
- vue创建路由,axios前后台交互,element-ui配置使用,django contentType组件
vue中创建路由 每一个vue组件都有三部分组成 template:放html代码 script:放js相关 style:放css相关 vue中创建路由 1.先创建组件 Course.vue 2.ro ...
- Django ContentType内置组件
一.引出问题 假如有这两张表,它们中的课程可能价格不一样.周期不一样.等等...不一样...,现在有一张价格策略表,怎么就用一张表报保存它们之间不同的数据呢? 可能你会这样: 确实是行!但是,如果有很 ...
随机推荐
- Centos内核参数优化
关于内核参数优化 net.ipv4.tcp_max_tw_buckets = 6000 net.ipv4.ip_local_port_range = 1024 65000 net.ipv4.tcp_ ...
- Pashmak and Parmida's problem(树状数组)
题目链接:http://codeforces.com/contest/459/problem/D 题意: 数列A, ai表示 i-th 的值, f(i,j, x) 表示[i,j]之间x的数目, 问:当 ...
- Elasticsearch从入门到放弃:文档CRUD要牢记
在Elasticsearch中,文档(document)是所有可搜索数据的最小单位.它被序列化成JSON存储在Elasticsearch中.每个文档都会有一个唯一ID,这个ID你可以自己指定或者交给E ...
- ubuntu 16.04上源码编译glog和gflags 编写glog-config.cmake和gflags-config.cmake | compile glog and glags on ubuntu 16.04
本文首发于个人博客https://kezunlin.me/post/977f5125/,欢迎阅读! compile glog and glags on ubuntu 16.04 Series comp ...
- 原创|我是如何从零学习开发一款跨平台桌面软件的(Markdown编辑器)
原始冲动 最近一直在学习 Electron 开发桌面应用程序,目的是想做一个桌面编辑器,虽然一直在使用Typore这款神器,但无奈Typore太过国际化,在国内水土不服,无法满足我的一些需求. 比如实 ...
- Netflix 开源 Polynote:对标 Jupyter,一个笔记本运行多种语言
谈到数据科学领域的开发工具,Jupyter 无疑是非常知名的一种.它具有灵活高效的特点,非常适合进行开发.调试.分享和教学.近日,Netflix(奈飞)居然也玩起了跨界,他们开源了一个名为 Polyn ...
- 《面试官之你说我听》:简明的图解Redis RDB持久化、AOF持久化
欢迎关注文章这一系列,一起学习 <提升能力,涨薪可待篇> <面试知识,工作可待篇> <实战演练,拒绝996篇> 如果此文对你有帮助.喜欢的话,那就点个赞呗,点个关注 ...
- 如何搭建Docker私有仓库
私有仓库 有时候使用 Docker Hub 这样的公共仓库可能不方便,用户可以创建一个本地仓库供私人使用. 本节介绍如何使用本地仓库. docker-registry 是官方提供的工具,可以用于构建私 ...
- Java基础IO类之缓冲流
首先要明确一个概念: 对文件或其他目标频繁的读写操作,效率低,性能差. 使用缓冲流的好处是:能够高效的读写信息,原理是先将数据先缓冲起来,然后一起写入或者读取出来. 对于字节: BufferedInp ...
- 【高可用架构】借助Envoy工具发布项目到多台服务器
前言 在上一篇,我们已经成功在开发机上部署了Deploy项目,下面我们继续在开发机上安装Envoy 两台应用服务器的IP 192.168.10.12 192.168.10.18 [高可用架构]系列链接 ...