一、什么是 ContentTypes

ContentTypesDjango 内置的一个应用,它可以追踪记录项目中所有 appmodel 的对应关系,并记录在 django_content_type 表中。

二、ContentTypes 的应用场景

ContentTypes 适用于一张表与多张表相关关联的场景,如:一个卖课程的网站,它主要售卖两类课程(普通课程和学位课程)。不同课程之间因学习周期不同,价格也不尽相同。因此就形成了每个课程可能有一个或多个价格策略。类似于下面这种:

如果我们自己设计表结构可能就会这样设计:

这样设计有个隐患,那就是当课程数目越来越多时,则意味着价格策略表 PricePolicy 必然要与多张表外键关联,导致越来越复杂,对查找造成很大的不便。

为此我们可以采取仅在 PricePolicy 中存储关联的表名称与相应课程 ID 的方式:

这样一来不论新增多少课程种类,我们只需在 PricePolicy 中新增表名和 ID 即可,其实这一步 ContentTypes 早已帮我们实现。

三、实践一:课程

需求一

  • 为学位课 DegreeCourse Python 全栈添加一条价格策略:1个月、12元
  • 为普泰课 Course rest_framework 添加一个新的价格策略:1个月、8 元

1、app/models.py

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey class Course(models.Model):
"""普通课程"""
title = models.CharField(max_length=64, verbose_name='课程名') class DegreeCourse(models.Model):
"""学位课程"""
title = models.CharField(max_length=64, verbose_name='课程名') class PricePolicy(models.Model):
"""价格策略"""
price = models.IntegerField()
period = models.IntegerField() # 自己实现
# table_name = models.CharField(verbose_name='关联的表名称')
# object_id = models.CharField(verbose_name='关联的表中数据行的 ID') # 使用 ContentType
content_type = models.ForeignKey(ContentType, verbose_name='关联普通课程或者学位课程', on_delete=models.CASCADE)
object_id = models.IntegerField(verbose_name='关联普通或学位课中的课程 ID') # 帮助快速实现 content_type 操作
content_object = GenericForeignKey('content_type', 'object_id') # 不会添加字段,只是为了插入或者查询时使用

2、app/urls.py

from django.urls import path, re_path
from api import views urlpatterns = [
path('test/', views.test, name='test'),
]

3、app/views.py

from django.shortcuts import render
from django.http import HttpResponse
from app import models from .models import DegreeCourse, ContentType, PricePolicy def test(request):
# 自己实现
# obj = DegreeCourse.objects.filter(title='Python 全栈').first()
# # content_type_id
# cobj = ContentType.objects.filter(model='course').first()
# # object_id
# PricePolicy.objects.create(price='11', period=3, content_type_id=cobj.id, object_id=obj.id) # 利用 contenttype 实现
# 为学位课 Python 全栈添加一个新的价格策略:一月 12 元
# obj1 = models.DegreeCourse.objects.filter(title='Python 全栈').first()
# models.PricePolicy.objects.create(price=12, period=2.5, content_object=obj1) # 为普通课 rest_framework 添加一个新的价格策略:一月 8 元
obj2 = models.Course.objects.filter(title='rest_framework').first()
models.PricePolicy.objects.create(price=8, period=1, content_object=obj2) return HttpResponse('OK')

由上可见,如果我们自己实现的话,需要先查找 content_type_idobject_id,而使用 ContentType 只需给 content_object 传值即可。


需求二

已知某个课程 ID,获取该课程,并获取该课程所有价格策略

1、app/models.py

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation class Course(models.Model):
"""普通课程"""
title = models.CharField(max_length=64, verbose_name='课程名') # 不会创建数据表列,仅用于反向查找
price_policy_list = GenericRelation('PricePolicy') # 新增 class DegreeCourse(models.Model):
"""学位课程"""
title = models.CharField(max_length=64, verbose_name='课程名') # 不会创建数据表列,仅用于反向查找
price_policy_list = GenericRelation('PricePolicy')

2、app/views.py

def test(request):
# 已知某个课程 ID,获取该课程,并获取该课程所有价格策略
course_obj = models.DegreeCourse.objects.filter(id=1).first()
price_policy = course_obj.price_policy_list.all() print(price_policy) for i in price_policy:
# 模型名称
model_name = models.ContentType.objects.filter(id=i.content_type_id).first().model
# 相应表字段ID,学位课程名
degree_course_name = models.DegreeCourse.objects.filter(id=i.object_id).first().title
# 价格
price = i.price
# 周期
period = i.period print(model_name, degree_course_name, price, period) return HttpResponse('OK')

四、实践二:商品优惠券

网上商城购物时,会有各种各样的优惠券,比如通用优惠券,满减券,或者是仅限特定品类的优惠券。在数据库中,可以通过外键将优惠券和不同品类的商品表关联起来:

1、app./models.py

from django.db import models

class Electrics(models.Model):
"""
id name
1 日立冰箱
2 三星电视
3 小天鹅洗衣机
"""
name = models.CharField(max_length=32) class Foods(models.Model):
"""
id name
1 面包
2 烤鸭
"""
name = models.CharField(max_length=32) class Clothes(models.Model):
name = models.CharField(max_length=32) class Coupon(models.Model):
"""
id name Electrics Foods Clothes more...
1 通用优惠券 null null null
2 冰箱满减券 2 null null
3 面包狂欢节 null 1 null """
name = models.CharField(max_length=32)
electric_obj = models.ForeignKey(to='Electrics', null=True)
food_obj = models.ForeignKey(to='Foods', null=True)
cloth_obj = models.ForeignKey(to='Clothes', null=True)

有些商品有优惠券,而有些商品没有,我们只用到模型中的一个或多个字段,就要给每个模型都添加外键关联。若商品类别有很多时,那么就需要添加很多个外键关联,显然这样是不行的。

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey class Electrics(models.Model):
"""
id name
1 日立冰箱
2 三星电视
3 小天鹅洗衣机
"""
name = models.CharField(max_length=32) coupons = GenericRelation(to='Coupon') # 添加反向关联,用于查找 class Foods(models.Model):
"""
id name
1 面包
2 烤鸭
"""
name = models.CharField(max_length=32) coupons = GenericRelation(to='Coupon') class Clothes(models.Model):
name = models.CharField(max_length=32) coupons = GenericRelation(to='Coupon') class Coupon(models.Model):
"""
id name Electrics Foods Clothes more...
1 通用优惠券 null null null
2 冰箱满减券 2 null null
3 面包狂欢节 null 1 null """
name = models.CharField(max_length=32) content_type = models.ForeignKey(to=ContentType) # step 1 添加一个字段,关联ContentType表中的某一个表
object_id = models.PositiveIntegerField() # step 2 添加一个id字段,就是‘某一张’表在ContentType里的对应的id
content_object = GenericForeignKey('content_type', 'object_id') # step 3 不会添加字段,只是为了插入或者查询时使用

2、app/views.py

from django.shortcuts import render, HttpResponse
from app import models
from django.contrib.contenttypes.models import ContentType def test(request):
content = ContentType.objects.filter(app_label='app', model='eletrics').first()
electrics_class = content.model_class() # electrics_class : models.Electrics
res = electrics_class.objects.all() # 为三星电视创建优惠券
s_tv = models.Electrics.objects.filter(id=2).first()
models.Coupon.objects.create(name='电视优惠券', content_object=s_tv) # 查询优惠券 id = 1 绑定了哪些商品
coupon_obj = models.Coupon.objects.filter(id=1).first()
prod = coupon_obj.content_object return HttpResponse('OK')

五、总结

  • 当一张表与多张表关联的时候可以考虑使用 ContentType
  • GenericForeignKey 仅为了辅助插入操作,不会添加新的字段
  • 如果要反向查找,可在模型中添加 GenericRelation,关联要反向查找的模型

Django 之 ContentType组件的更多相关文章

  1. Django之ContentType组件

    一.理想表结构设计 1.初始构建 1. 场景刚过去的双12,很多电商平台都会对他们的商品进行打折促销活动的,那么我们如果要实现这样的一个场景,改如何设计我们的表? 2. 初始表设计 注释很重要,看看吧 ...

  2. Django组件(五) Django之ContentType组件

    基础使用 -contenttype组件 -django提供的一个快速连表操作的组件,可以追踪项目中所有的APP和model的对应关系,并记录在ContentType表中. 当我们的项目做数据迁移后,会 ...

  3. Django day31 contentType组件,Django的缓存

    一.contentType组件 1.干什么用的? 是一个django内置的一个组件,方便我们快速的连表操作 2.这两个字段都不会在数据库中生成,他只是用来查询,插入的 -在course表中: poli ...

  4. 【Django】ContentType组件

    目录 理解 表结构 使用 @ 好,现在我们有这样一个需求,我们的商城里有很多的商品,然而节日要来了,我们要搞活动. 那么,我们就要设计优惠券,优惠券都有什么类型呢?满减的.折扣的.立减的.等等等... ...

  5. Django中content-type组件

    django-content 1.需求 一家餐馆,有多个菜系,粤菜.湘菜.闽南菜.东北菜等,每个菜系中的菜品又分为小份.中份.大份,每个菜系对应的菜品量价格不同,现需要将该需求建表. 2. 建表方式 ...

  6. Django中content-type组件的使用

    content-type组件 ContentType是Django的内置的一个应用,可以追踪项目的所有APP和model的对应关系,并记录在ContentTpe表中,当我们的项目做数据迁移后,会有很多 ...

  7. Django :Content-Type组件

    Content_Type 组件 用法: model.py: from django.db import models # Create your models here. class Food(mod ...

  8. Django:ContentType组件

    一.项目背景 二.版本一 三.版本二 三.终极版(使用ContentType) 一.项目背景 luffy项目,有课程有学位课(不同的课程字段不一样),价格策略 问题:1.如何设计表结构,来表示这种规则 ...

  9. django内置组件——ContentTypes

    一.什么是Django ContentTypes? Django ContentTypes是由Django框架提供的一个核心功能,它对当前项目中所有基于Django驱动的model提供了更高层次的抽象 ...

随机推荐

  1. dijkstra算法的堆优化

    普通的dijkstra算法模板: //数据结构 int g[LEN][LEN]; //邻接矩阵 int vis[LEN]; //标记是否访问 int dist[LEN] //源点到各点的距离 fill ...

  2. C语言博客作业04—数组

    0.展示PTA总分(0----2) 展示3张关于"数组题目集"分数截图. 1.本章学习总结(2分) 1.1 学习内容总结 整理数组这章学习主要知识点,必须包含内容有: (1)数组查 ...

  3. vultr的防火墙注意事项

    如下图所示,你设置让任意IP的TCP,UDP,GRE,ESP,ICMP都允许访问,并不表示开放了任意协议和端口了. 下图只是表示开放了TCP,UDP,GRE,ESP,ICMP五个协议,比如ROS路由的 ...

  4. Spring Cloud @RefreshScope 原理是什么?

    要清楚RefreshScope,先要了解Scope Scope(org.springframework.beans.factory.config.Scope)是Spring 2.0开始就有的核心的概念 ...

  5. MySQL 执行插入报错 - Incorrect string value: '\xE4\xB8\xAD\xE6\x96\x87' for column 'name' at row 1

    报错的原因就是在执行插入时对Name这个字段被赋予了错误的字符串值:’\xE4\xB8\xAD\xE6\x96\x87’ 实际上就函数里面的变量接收到的值编码格式跟它定义的不一致.  使用navica ...

  6. spring boot2X集成spring cloud config

    Spring Cloud Config 分为 Config Server: 分布式配置中心,是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息 Config Client: 通过指定 ...

  7. centos7 df 命令卡死

    登录服务器想查看磁盘使用情况,使用了df,但卡住半天没有响应. 运行strace df -h,发现最后卡在了 stat("/proc/sys/fs/binfmt_misc", 无法 ...

  8. Keras 入门实例

    使用Keras构建神经网络的基本工作流程主要可以分为 4个部分.(而这个用法和思路,很像是在使用Scikit-learn中的机器学习方法) Model definition → Model compi ...

  9. Python【每日一问】30

    问: [基础题]:一个足球队在寻找年龄在10岁到12岁的小女孩(包括10岁和12岁)加入.编写一个程序,询问用户的性别(m表示男性,f表示女性)和年龄,然后显示一条消息指出这个人是否可以加入球队,询问 ...

  10. windows x64安装与测试redis

    说明:安装与测试的系统为windows X64: 1.下载redis:https://github.com/microsoftarchive/redis/releases 2.解压Redis-x64- ...