「Django」contenttypes基本用法
当一张表和多个表ForeignKey关联,并且多个FK中只能选择其中一个或其中n个时,可以利用contenttypes,只需定义三个字段就搞定!
contenttypes 是Django内置的一个应用,可以追踪项目中所有app和model的对应关系,并记录在ContentType表中。
每当我们创建了新的model并执行数据库迁移后,ContentType表中就会自动新增一条记录。比如我在应用app的models.py中创建表class Electrics(models.Model): pass。从数据库查看ContentType表,显示如下:

那么这个表有什么作用呢?这里提供一个场景,网上商城购物时,会有各种各样的优惠券,比如通用优惠券,满减券,或者是仅限特定品类的优惠券。在数据库中,可以通过外键将优惠券和不同品类的商品表关联起来:
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)
常规写法
如果是通用优惠券,那么所有的ForeignKey为null,如果仅限某些商品,那么对应商品ForeignKey记录该商品的id,不相关的记录为null。但是这样做是有问题的:实际中商品品类繁多,而且很可能还会持续增加,那么优惠券表中的外键将越来越多,但是每条记录仅使用其中的一个或某几个外键字段。
通过使用contenttypes 应用中提供的特殊字段GenericForeignKey,我们可以很好的解决这个问题。只需要以下三步:
- 在model中定义ForeignKey字段,并关联到ContentType表。必须这个字段命名为“content_type”
- 在model中定义PositiveIntegerField字段,用来存储关联表中的主键。必须这个字段命名为“object_id”,否则GenericRelation反向查找的时候会报错
- 在model中定义GenericForeignKey字段,传入上述两个字段的名字。
为了更方便查询商品的优惠券,我们还可以在商品类中通过GenericRelation字段定义反向关系。
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey class Electrics(models.Model):
name = models.CharField(max_length=32)
coupons = GenericRelation(to='Coupon') # 用于反向查询,不会生成表字段 def __str__(self):
return self.name class Foods(models.Model):
name = models.CharField(max_length=32)
coupons = GenericRelation(to='Coupon') def __str__(self):
return self.name class Clothes(models.Model):
name = models.CharField(max_length=32)
coupons = GenericRelation(to='Coupon') def __str__(self):
return self.name class Coupon(models.Model):
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 不会添加字段,只是为了插入或者查询时使用 def __str__(self):
return self.name
contenttype写法
from django.shortcuts import render, HttpResponse
from app01 import models
from django.contrib.contenttypes.models import ContentType def test(request):
if request.method == 'GET':
# ContentType表对象有model_class() 方法,取到对应model
content = ContentType.objects.filter(app_label='app01', model='electrics').first() # 表名小写
cloth_class = content.model_class() # cloth_class 就相当于models.Electrics
res = cloth_class.objects.all()
print(res) # 为三星电视(id=2)创建一条优惠记录
s_tv = models.Electrics.objects.filter(id=2).first()
models.Coupon.objects.create(name='电视优惠券', content_object=s_tv) #直接用content_object属性添加,无需给contenttype和object_id字段分别赋值
<br> # 查询优惠券(id=1)绑定了哪些商品
coupon_obj = models.Coupon.objects.filter(id=1).first()
prod = coupon_obj.content_object
print(prod) # 查询三星电视(id=2)的所有优惠券
res = s_tv.coupons.all() print(res) # 查询obj的所有优惠券:如果没有定义反向查询字段,通过如下方式:
content = ContentType.objects.filter(app_label='app01', model='model_name').first()
res = models.OftenAskedQuestion.objects.filter(content_type=content,
object_id=obj.pk).all() <br> <br><br> return HttpResponse('....')
记录和查询
------------------------------------------------------------------------------------------------------------------------------------------------
PS:在model中定义PositiveIntegerField字段,必须用“object_id”,否则GenericRelation反向查找的时候会报错!!!
「Django」contenttypes基本用法的更多相关文章
- 「Django」学习之路,持续更改
一.setting设置 1.设置 局域网可以部署连接 ALLOWED_HOSTS = ['*.besttome.com','192.168.1.100'] 2.static配置 STATIC_URL ...
- 「Django」rest_framework学习系列-API访问跨域问题
#以中间件方式解决API数据访问跨域问题1.API下新建文件夹下写PY文件a.引入内置类继承: from django.middleware.common import MiddlewareMixin ...
- 「Django」rest_framework学习系列-序列化
序列化方式一 :在业务类里序列化数据库数据 class RolesView(APIView): def get(self,request,*args,**kwargs): roles = models ...
- 「Django」数据库访问优化
先做性能分析 - 两个工具 django.db.connection from django.db import connection# contextprint connection.queries ...
- 「Django」与mysql8连接的若干问题
1.setting配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', #数据库驱动名 'NAME': 'my_tes ...
- 「Django」Django内置email发送邮件
Django内置email发送邮件 1.首先在settings.py文件设置相关参数 STATIC_URL = '/static/' # 设置邮件域名 EMAIL_HOST = 'smtp.163.c ...
- 「Django」Xadmin应用
第一:命令安装xadmin2 pip install xadmin2 第二:setting.py中设置 INSTALLED_APPS INSTALLED_APPS = ( ... 'xadmin', ...
- 「Django」浏览+1的操作
适应于网页.文章等浏览次数统计 1.Models设置:添加viewed方法 class NewsTitle(models.Model): title = models.CharField(max_le ...
- 「Vue」watch基本用法
应用于监视路由地址改变,如有新地址(即路由地址改变)即执行自定义方法 methods: { itemShow() { this.$axios.get('item/item/'+this.id+'?to ...
随机推荐
- mfc 动态为控件添加事件2
重载窗口过程 为动态控件绑定事件 一.重载窗口过程处理函数 CWnd::WindowProc virtual LRESULT WindowProc( UINT message, WPARAM wPar ...
- CF434D Nanami's Power Plant
就是切糕那道题,首先对每个函数连一串,然后\(x_u\leq x_v+d\)这个条件就是\(u\)函数\(i\)取值连向\(v\)函数\(i-d\)取值边权为inf,然后答案就是最小割了. #incl ...
- maven 相关问题
maven 这里要更新完 不一定非要clean install 那个出问题了再弄,一般刷新一下maven仓库就行了,最好还是用自己配置的maven,不容易出问题
- mybatis 初步使用(IDEA的Maven项目, 超详细)
目录 创建 Maven 项目 Maven配置 pom.xml 创建数据库 配置Mybatis 配置mybatis的XML文件 创建实体类和对应的Mapper.xml 测试 源码 @ 创建 Maven ...
- Git提交空目录
1.git仅跟踪文件的变动,不跟踪目录.如果需要提交空目录,可以在里面添加 .gitignore 文件,方法如下: find . -type d -empty -exec touch {}/.giti ...
- Python_xlutils.copy
import xlrd import xlwt from xlutils.copy import copy # 读取工作簿 objWB = xlrd.open_workbook(r'C:\Users\ ...
- Markdown语言学习
看够了单一的文本文档么?或者写一个word各种调整样式?试试Markdown吧! Markdown是一种文本标记语言,通过简单的标记语法,使单一的文本内容具有一定的格式. 下面来看看常用的各种标记吧 ...
- opencv core组件进阶
1.图像在内存中存储方式,图像矩阵的大小取决于颜色模型,取决于所有的通道数:还有重要的颜色空间缩减的概念:因为如果是RGB的话,使用uchar的话,就有256^3的结合方法.所以要用到颜色缩减的方法, ...
- 《在kali上完成gdb调试》
kali使用流程 1.使menuos停止 方法如图: 效果如图: 2.启动调试 打开一个新的命令行,然后方法如下图: 3.设置断点 注:由图可看出,断点设置在sys_clone,dup_task_st ...
- Linux内核分析(第六周)
进程的控制与创建 一.进程的描述 1.操作系统内核的三大功能:进程管理(核心),内存管理,文件系统: 2.状态: fork() task_zombit(终止) task_running(就绪:但是没有 ...