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自定义类并使用的方法
May 07 Python
python和opencv实现抠图
Jul 18 Python
python实现傅里叶级数展开的实现
Jul 21 Python
python 获取键盘输入,同时有超时的功能示例
Nov 13 Python
在Pycharm中执行scrapy命令的方法
Jan 16 Python
python写程序统计词频的方法
Jul 29 Python
Matplotlib使用Cursor实现UI定位的示例代码
Mar 12 Python
Python新手学习raise用法
Jun 03 Python
keras.utils.to_categorical和one hot格式解析
Jul 02 Python
python 基于DDT实现数据驱动测试
Feb 18 Python
python编程项目中线上问题排查与解决
Nov 01 Python
在python中读取和写入CSV文件详情
Jun 28 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
php SQL防注入代码集合
2008/04/25 PHP
收藏的PHP常用函数 推荐收藏保存
2010/02/21 PHP
PHP中限制IP段访问、禁止IP提交表单的代码
2011/04/23 PHP
浅析PHP绘图技术
2013/07/03 PHP
php实现的Captcha验证码类实例
2014/09/22 PHP
php实现统计目录文件大小的函数
2015/12/25 PHP
PHP使用fopen与file_get_contents读取文件实例分享
2016/03/04 PHP
PHP的Yii框架中行为的定义与绑定方法讲解
2016/03/18 PHP
PHP根据树的前序遍历和中序遍历构造树并输出后序遍历的方法
2017/11/10 PHP
详解php与ethereum客户端交互
2018/04/28 PHP
PHP+redis实现的悲观锁机制示例
2018/06/12 PHP
PHP实现的AES加密、解密封装类与用法示例
2018/08/02 PHP
js修改input的type属性及浏览器兼容问题探讨与解决
2013/01/23 Javascript
如何用JavaScript动态呼叫函数(两种方式)
2013/05/03 Javascript
jQuery表单获取和失去焦点输入框提示效果的实例代码
2013/08/01 Javascript
jQuery鼠标悬浮链接弹出跟随图片实例代码
2016/01/08 Javascript
详解JavaScript中数组和字符串的lastIndexOf()方法使用
2016/03/13 Javascript
详解vue-router 路由元信息
2017/09/13 Javascript
在Vue中使用CSS3实现内容无缝滚动的示例代码
2020/11/27 Vue.js
JS+CSS实现过渡特效
2021/01/02 Javascript
[20:21]《一刀刀一天》第十六期:TI国际邀请赛正式打响,总奖金超过550万
2014/05/23 DOTA
使用基于Python的Tornado框架的HTTP客户端的教程
2015/04/24 Python
查看Python安装路径以及安装包路径小技巧
2015/04/28 Python
python从sqlite读取并显示数据的方法
2015/05/08 Python
python 回调函数和回调方法的实现分析
2016/03/23 Python
python实现按行切分文本文件的方法
2016/04/18 Python
python 使用pygame工具包实现贪吃蛇游戏(多彩版)
2019/10/30 Python
Python常用模块sys,os,time,random功能与用法实例分析
2020/01/07 Python
python实现对变位词的判断方法
2020/04/05 Python
Python3+SQLAlchemy+Sqlite3实现ORM教程
2021/02/16 Python
船餐厅和泰晤士河餐饮游轮:Bateaux London
2018/03/19 全球购物
党的群众路线教育实践活动总结材料
2014/10/30 职场文书
2014年药品销售工作总结
2014/12/16 职场文书
公务员政审个人总结
2015/02/12 职场文书
Mysql存储过程、触发器、事件调度器使用入门指南
2022/01/22 MySQL
python实现简单的三子棋游戏
2022/04/28 Python