Django中的contenttypes组件
个人博客中总结的
需求
有“专题课”跟“学位课”两种课程,每个课程都根据周期的不同价格也跟着变动。
不推荐的设计方法
“专题课”根据周期的不同课程的价格也不一样——用第三张表“课程价格表”与“专题课”这张表做外键关联。
“专题课表”与“价格表”建立外键关联的关系如下:
“学位课表”与“价格表”建立外键的关系如下:
但是,这样实现的话很繁琐 —— 实际中我们不这么设计!
价格表用一张表实现的方式
在价格表中加课程字段:
—— 这样设计的话:以后再增加课程(跟学位课与专题课是一个级别的大分类的课,就在价格表中增加对应的字段就好了)
—— 这样设计随着分类的增多数据库中表的字段也随之增多,从上图可以看出,每个字段还有很多无效的数据,这是我们在实际设计中需要避免的!
Django的contenttypes组件
另外的思路
如上图所示:右边那张表记录着“大的类别表”,中间的表的table_id外键关联的是右边的表;xid代表的是“大的类别表下面具体的课程id”,以后再拓展的时候数据库中不会再出现多余没有用的数据了。
右边那张表Django自动创建
只要项目中注册了contenttypes这个组件,我们在数据库迁移时,Django会为我们自动生成contenttypes表,其中就有上面图中最右边的对应关系——所以最右边那张表我们不用自己创建了:
models.py中的设计如下
from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation class DegreeCourse(models.Model): """学位课程""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255, verbose_name="缩略图") brief = models.TextField(verbose_name="学位课程简介", ) class Course(models.Model): """专题课程""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255) # 不会在数据库生成列,只用于帮助你进行查询
# (3)找到某个课程所关联的所有的价格策略 —— GenericRelation
# obj = models.Course.objects.get(pk=1)
# for i in obj.policy_list.all():
# print(i.pk,i.valid_valid_period,i.price)
policy_list = GenericRelation("PricePolicy") class PricePolicy(models.Model): """价格与有课程效期表"""
# 与ContentType表做关联 —— 找到大的分类 content_type = models.ForeignKey(ContentType) # 关联course or degree_course
# 对应"大分类中的课程的id",用正整数表示 —— 名字必须叫object_id
object_id = models.PositiveIntegerField()
#不会在数据库生成列,只用于帮助你进行添加和查询
# 不会在数据库中生成列,只是帮我们添加与查询查询数据用的
# (1)添加数据的时候用这个,指定:content_object = models.SeniorCourse.objects.get(pk=2),
# (2)查询的时候用这个,自动找到"大分类"中的某一行
# obj = models.PricePolicy.objects.get(content_type=9,object_id=2)
# print(obj.content_object.name)
content_object = GenericForeignKey('content_type', 'object_id')
valid_period_choices = ( (1, '1天'), (3, '3天'), (7, '1周'), (14, '2周'), (30, '1个月'), (60, '2个月'), (90, '3个月'), (180, '6个月'), (210, '12个月'), (540, '18个月'), (720, '24个月'), )
valid_period = models.SmallIntegerField(choices=valid_period_choices) price = models.FloatField()
视图函数如下
from django.shortcuts import render,HttpResponse from app01 import models from django.contrib.contenttypes.models import ContentType def test(request): # 1.在价格策略表中添加一条数据 models.PricePolicy.objects.create( valid_period=7, price=6.6, content_type=ContentType.objects.get(model='course'), object_id=1 ) models.PricePolicy.objects.create( valid_period=14, price=9.9, content_object=models.Course.objects.get(id=1) ) # 2. 根据某个价格策略对象,找到他对应的表和数据,如:管理课程名称 price = models.PricePolicy.objects.get(id=2) print(price.content_object.name) # 自动帮你找到 # 3.找到某个课程关联的所有价格策略 obj = models.Course.objects.get(id=1) for item in obj.policy_list.all(): print(item.id,item.valid_period,item.price
# 4、定义了GenericRelation,删除SeniorCourse中的数据价格策略中的数据也没了!!!
# 删除了SeniorCourse中的一个课程后,PricePublic中与之相关的数据都删除了!!!
models.SeniorCourse.objects.get(pk=1).delete()
return HttpResponse
路由简单
from django.contrib import admin from django.urls import path from testapp import views urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index,name='index'), ]
~~~
GenericForeignKey与GenericRelation详细讲解见我的个人博客
—— 其实说白了,前者是通过价格策略表去查对应的课程表中的信息;后者是通过具体的课程查询对应的价格策略的!
contenttypes的功能 *****
1、创建ContentType表 —— 价格策略表与这张表建立外键关系~~能拿到“大分类表”的名称
2、GenericForeignKey —— 在价格策略表中每个大分类下的具体课程与不同的课程之间建立外键关联
3、GenericReleation —— 与2正好相反,拿到课程对象去找对应的价格策略。
4、定义了GenericRelation,删除SeniorCourse中的数据价格策略中的数据也没了!!!
5、contenttypes使用场景:一张表与N张表动态的创建ForeignKey关系~创建好多列的话会有很多空的无效的数据
—— 通过contenttypes做的话会很灵活!
其他使用场景 *****
———— 优惠券业务,也能使用contenttypes,优惠券可以跟很多课程表进行关联~
———— 评论:课程评论、视频评论、讨论区评论~~