Django contenttypes 框架详解(小结)


Posted in Python onAugust 13, 2018

一、什么是Django ContentTypes?

Django ContentTypes是由Django框架提供的一个核心功能,它对当前项目中所有基于Django驱动的model提供了更高层次的抽象接口。 当然我们不是说的是http中的content-type!完全没有任何关系!

下面将一步一步解释Django ContentTypes在Django框架中做了什么,以及如何使用Django ContentTypes。 

当然,如果对于ContentTypes有了初步了解而只是不了解它的应用场景,可以直接查阅一下原文档:

https://docs.djangoproject.com/en/1.10/ref/contrib/contenttypes/

二、Django ContentTypes做了什么?

当使用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是在django.contrib.auth之后,这是因为auth中的permission系统是根据contenttypes来实现的。

我们来查询查阅了一下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

大家可以看到ContentType就是一个简单的django model,而且它在数据库中的表的名字为django_content_type。

这个表的名字一般都不会陌生,在第一次对Django的model进行migrate之后,就可以发现在数据库中出现了一张默认生成的名为django_content_type的表。

如果没有建立任何的model,默认django_content_type是这样的:

Django contenttypes 框架详解(小结)

因此,django_content_type记录了当前的Django项目中所有model所属的app(即app_label属性)以及model的名字(即model属性)。

当然,django_content_type并不只是记录属性这么简单,contenttypes是对model的一次封装,

因此可以通过contenttypes动态的访问model类型,而不需要每次import具体的model类型。

  • ContentType实例提供的接口 
    • ContentType.model_class() 
      • 获取当前ContentType类型所代表的模型类
    • ContentType.get_object_for_this_type() 
      • 使用当前ContentType类型所代表的模型类做一次get查询
    • ContentType管理器(manager)提供的接口 
    • ContentType.objects.get_for_id() 
      • 通过id寻找ContentType类型,这个跟传统的get方法的区别就是它跟get_for_model共享一个缓存,因此更为推荐。
    • ContentType.objects.get_for_model() 
      • 通过model或者model的实例来寻找ContentType类型

三、Django ContentTypes的使用场景

在我们这个项目中各种商品的优惠卷就运用到了这个知识点:

假使我们models下有这几张表:

class Electrics(models.Model): #电器类
  name = models.CharField(max_length=32)
  price= models.IntegerField(default=100)

  def __str__(self):
    return self.name


class Foods(models.Model):   #食物类
  name = models.CharField(max_length=32)
  price = models.IntegerField(default=100)

  def __str__(self):
    return self.name


class Clothes(models.Model):  #衣服类
  name = models.CharField(max_length=32)
  price= models.IntegerField(default=100)
  def __str__(self):
    return self.name

class Coupon(models.Model):  #优惠券
  name = models.CharField(max_length=32)

  def __str__(self):
    return self.name

我们先来考虑一个问题,如何把这些商品和优惠卷相关联?

一种商品一个优惠卷,那我们就在表中加入一种商品的优惠券,就是一个一对多的ForeignKey,那么多个商品就有各种优惠卷,

但是一种商品的特定优惠卷在表结构中,就那个字段有值,别的不相关的记录为null,而且每增加一个商品,又要手动的去添加外键,

这是繁琐的!

所以我们就使用contenttypes 应用中提供的特殊字段GenericForeignKey,我们可以解决上面的问题:

只需要以下三步:

  • 在model中定义ForeignKey字段,并关联到ContentType表。通常这个字段命名为“content_type”
  • 在model中定义PositiveIntegerField字段,用来存储关联表中的主键。通常这个字段命名为“object_id”
  • 在model中定义GenericForeignKey字段,传入上述两个字段的名字。

具体实例代码:

class Coupon(models.Model):
  name = models.CharField(max_length=32)

  content_type = models.ForeignKey(to=ContentType) # step 1
  object_id = models.PositiveIntegerField() # step 2
  content_object = GenericForeignKey('content_type', 'object_id') # step 3

  def __str__(self):
    return self.name

这样的话不管表的数据都可以查询出来,而且添加新的商品的商品,也不需要动优惠券的源码。

但我们在查询的过程中,用ORM实在太繁琐了,所以还有一个反向查询的方法:

就是在每个商品中关联 绑定一个关系:

coupons = GenericRelation(to='Coupon') # 用于反向查询,不会生成表字段

这样我们就可以直接ORM的.coupons找相应的字段!

Python 相关文章推荐
Python中字典的setdefault()方法教程
Feb 07 Python
Python实现可设置持续运行时间、线程数及时间间隔的多线程异步post请求功能
Jan 11 Python
python配置文件写入过程详解
Oct 19 Python
python正则表达式匹配IP代码实例
Dec 28 Python
Python类如何定义私有变量
Feb 03 Python
Windows系统下pycharm中的pip换源
Feb 23 Python
基于Python3.7.1无法导入Numpy的解决方式
Mar 09 Python
Python 日期时间datetime 加一天,减一天,加减一小时一分钟,加减一年
Apr 16 Python
浅谈tensorflow中dataset.shuffle和dataset.batch dataset.repeat注意点
Jun 08 Python
Python使用for生成列表实现过程解析
Sep 22 Python
Django和Ueditor自定义存储上传文件的文件名
Feb 25 Python
Python中异常处理用法
Nov 27 Python
Python中的Numpy矩阵操作
Aug 12 #Python
浅谈python之新式类
Aug 12 #Python
详解Django中类视图使用装饰器的方式
Aug 12 #Python
python中pip的安装与使用教程
Aug 10 #Python
python3判断url链接是否为404的方法
Aug 10 #Python
Python实现数据可视化看如何监控你的爬虫状态【推荐】
Aug 10 #Python
Selenium元素的常用操作方法分析
Aug 10 #Python
You might like
ThinkPHP CURD方法之data方法详解
2014/06/18 PHP
php字符串比较函数用法小结(strcmp,strcasecmp,strnatcmp及strnatcasecmp)
2016/07/18 PHP
PHP实现将上传图片自动缩放到指定分辨率,并保持清晰度封装类示例
2019/06/17 PHP
laravel 中某一字段自增、自减的例子
2019/10/11 PHP
分享几种好用的PHP自定义加密函数(可逆/不可逆)
2020/09/15 PHP
Gambit vs CL BO3 第三场 2.13
2021/03/10 DOTA
jQuery 表单验证插件formValidation实现个性化错误提示
2009/06/23 Javascript
jquery插件制作 提示框插件实现代码
2012/08/17 Javascript
js之事件冒泡和事件捕获详细介绍
2013/10/28 Javascript
jquery解析XML字符串和XML文件的方法说明
2014/02/21 Javascript
js函数名与form表单元素同名冲突的问题
2014/03/07 Javascript
jQuery中html()方法用法实例
2014/12/25 Javascript
AngularJS 遇到的小坑与技巧小结
2016/06/07 Javascript
通过扫描二维码打开app的实现代码
2016/11/10 Javascript
用原生js做单页应用
2017/01/17 Javascript
基于JS实现限时抢购倒计时间表代码
2017/05/09 Javascript
使用jQuery实现简单的tab框实例
2017/08/22 jQuery
浅析Javascript中双等号(==)隐性转换机制
2017/10/27 Javascript
mongoose设置unique不生效问题的解决及如何移除unique的限制
2017/11/07 Javascript
JavaScript实现的鼠标跟随特效示例【2则实例】
2018/12/22 Javascript
详解vue-cli3开发Chrome插件实践
2019/05/29 Javascript
vuex+axios+element-ui实现页面请求loading操作示例
2020/02/02 Javascript
jQuery实现查看图片功能
2020/12/01 jQuery
python函数中return后的语句一定不会执行吗?
2017/07/06 Python
通过Python实现自动填写调查问卷
2017/09/06 Python
Python3操作SQL Server数据库(实例讲解)
2017/10/21 Python
Python内建模块struct实例详解
2018/02/02 Python
python中ImageTk.PhotoImage()不显示图片却不报错问题解决
2018/12/06 Python
浅谈在django中使用filter()(即对QuerySet操作)时踩的坑
2020/03/31 Python
Python环境使用OpenCV检测人脸实现教程
2020/10/19 Python
关于探究python中sys.argv时遇到的问题详解
2021/02/23 Python
详解HTML5中的picture元素响应式处理图片
2018/01/03 HTML / CSS
Herve Leger官网:标志性绷带连衣裙等
2018/12/26 全球购物
如何拷贝一整个Java对象,包括它的状态
2013/12/27 面试题
导游词之铁岭象牙山
2019/12/06 职场文书
CSS布局之浮动(float)和定位(position)属性的区别
2021/09/25 HTML / CSS