一, 什么是 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_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 找相应的字段!
来源: https://www.cnblogs.com/ManyQian/p/9463796.html