Django 不通过外键实现多表关联查询
Django不通过外键实现多表关联查询
by:授客 QQ:1033553122
测试环境
Win 10
Python 3.5.4
Django-2.0.13.tar.gz
需求
不通过外键,使用django orm语法实现多个表之间的关联查询,类似如下sql的查询效果:
SELECT tb_project_version.*, tb_sprint.name, tb_project.name
FROM tb_project_version
JOIN tb_sprint ON tb_sprint.id=tb_project_version.sprint_id
JOIN tb_project ON tb_project.id=tb_project_version.project_id
数据表Model设计
class Sprint(models.Model):
id = models.AutoField(primary_key=True, verbose_name='自增id')
name = models.CharField(max_length=50, verbose_name='迭代名称')
...略
class Meta:
db_table = 'tb_sprint'
verbose_name = '产品迭代表'
verbose_name_plural = verbose_name
class Project(models.Model):
id = models.AutoField(primary_key=True, verbose_name='自增id')
name = models.CharField(max_length=50, verbose_name='项目名称')
...略
class Meta:
db_table = 'tb_project'
verbose_name = '项目表'
verbose_name_plural = verbose_name
class ProjectVersion(models.Model):
id = models.AutoField(primary_key=True, verbose_name='自增id')
name = models.CharField(max_length=50, verbose_name='版本名称')
project_id = models.IntegerField(verbose_name='关联的项目ID')
sprint_id = models.IntegerField(verbose_name='关联的迭代ID')
...略
class Meta:
db_table = 'tb_project_version'
verbose_name = '项目版本表'
verbose_name_plural = verbose_name
实现方法1-通过extra api函数实现
如下,带背景色部分的内容为核心
serializers.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework import serializers
from backend.models import ProjectVersion
# ProjectVersion model 序列化器
class ProjectVersionSerializer(serializers.ModelSerializer):
project = serializers.CharField(required=True)
sprint = serializers.CharField(required=True)
class Meta:
model = ProjectVersion
fields = '__all__'
read_only_fields = ['project', 'sprint']
说明:如上,如果使用了django rest framework序列化,则需要为其序列化器添加model中不存在的字段,否则序列化后还是看不到对应的目标字段
project_version_views.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = '授客'
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from backend.models import ProjectVersion
from backend.serializers import ProjectVersionSerializer
class ProjectVersionListAPIView(APIView):
'''
项目视图-版本管理
'''
# 查询列表数据
def get(self, request, format=None):
result = {}
try:
params = request.GET
page_size = int(params.get('pageSize'))
page_no = int(params.get('pageNo'))
name = params.get('name')
project_id = params.get('projectId')
sort = params.get('sort')
if sort:
sort_list = sort.split(',')
else:
sort_list = ['-id']
startIndex = (page_no - 1) * page_size
endIndex = startIndex + page_size
filters = {'is_delete':0}
if name:
filters['name__startswith'] = name
if project_id:
filters['project_id'] = project_id
projectVersions = ProjectVersion.objects.filter(**filters).extra(
select={'project': 'SELECT tb_project.name FROM tb_project WHERE tb_project.id = tb_project_version.project_id',
'sprint':'SELECT tb_sprint.name FROM tb_sprint WHERE tb_sprint.id = tb_project_version.sprint_id'},
)
rows = projectVersions.order_by(*sort_list)[startIndex:endIndex]
rows = ProjectVersionSerializer(rows, many=True).data
total = projectVersions.count()
result['msg'] = '获取成功'
result['success'] = True
result['data'] = {}
result['data']['rows'] = rows
result['data']['total'] = total
return Response(result, status.HTTP_200_OK)
except Exception as e:
result['msg'] = '%s' % e
result['success'] = False
return Response(result, status.HTTP_500_INTERNAL_SERVER_ERROR)
说明:
projectVersions.order_by(*sort_list)[startIndex:endIndex]
等价于
SELECT (SELECT tb_project.name FROM tb_project WHERE tb_project.id = tb_project_version.project_id) AS `project`,
(SELECT tb_sprint.name FROM tb_sprint WHERE tb_sprint.id = tb_project_version.sprint_id) AS `sprint`,
`tb_project_version`.`id`,
`tb_project_version`.`name`,
`tb_project_version`.`project_id`,
`tb_project_version`.`sprint_id`,
...略
FROM `tb_project_version`
WHERE `tb_project_version`.`is_delete` = 0
ORDER BY `tb_project`.`id` DESC LIMIT 10 # 假设startIndex=0, endIndex=10
projectVersions.count()
等价于
SELECT COUNT(*) AS `__count` FROM `tb_project_version`
WHERE `tb_project_version`.`is_delete` = 0
上述查询代码的另一种实现
projectVersions = Project.objects.filter(**filters).extra(
select={'project:'tb_project.name',
'sprint':' tb_sprint.name',
tables=['tb_project', 'tb_sprint'],
where=['tb_project.id=tb_project_version.project_id', 'tb_sprint.id = tb_project_version.sprint_id']
)
rows = projectVersions.order_by(*sort_list)[startIndex:endIndex]
rows = ProjectVersionSerializer(rows, many=True).data
total = projectVersions.count()
projectVersions.order_by(*sort_list)[startIndex:endIndex]
等价于
SELECT (tb_project.name) AS `project`,
(tb_sprint.name) AS `sprint`,
`tb_project_version`.`id`,
`tb_project_version`.`name`,
`tb_project_version`.`project_id`,
`tb_project_version`.`sprint_id`,
...略
FROM `tb_project_version`
WHERE `tb_project_version`.`is_delete` = 0 AND (tb_project.id=tb_project_version.project_id) AND (tb_sprint.id = tb_project_version.sprint_id)
ORDER BY `tb_project`.`id` DESC LIMIT 10 # 假设startIndex=0, endIndex=10
projectVersions.count()
等价于
SELECT COUNT(*) AS `__count` FROM `tb_project_version` , `tb_project` , `tb_sprint` WHERE `tb_project_version`.`is_delete` = 0 AND (tb_project.id=tb_project_version.project_id) AND (tb_sprint.id = tb_project_version.sprint_id)
实现方法2-通过django rest framework实现
serializers.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework import serializers
from backend.models import ProjectVersion
from backend.models import Sprint
from backend.models import Project
# ProjectVersion model 序列化器
class ProjectVersionSerializer(serializers.ModelSerializer):
project = serializers.SerializerMethodField()
sprint = serializers.SerializerMethodField()
def get_sprint(self, obj):
"""
:param obj: 当前ProjectVersion的实例
"""
current_project_version = obj
sprint = Sprint.objects.filter(id=current_project_version.sprint_id).first()
if sprint:
return sprint.name
else:
return '--'
def get_project(self, obj):
"""
:param obj: 当前ProjectVersion的实例
"""
current_project_version = obj
project = Project.objects.filter(id=current_project_version.project_id).first()
if project:
return project.name
else:
return '--'
class Meta:
model = ProjectVersion
fields = '__all__'
read_only_fields = ['project', 'sprint']
project_version_views.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = '授客'
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from backend.models import ProjectVersion
from backend.serializers import ProjectVersionSerializer
class ProjectVersionListAPIView(APIView):
'''
项目视图-版本管理
'''
# 查询列表数据
def get(self, request, format=None):
result = {}
try:
params = request.GET
page_size = int(params.get('pageSize'))
page_no = int(params.get('pageNo'))
name = params.get('name')
project_id = params.get('projectId')
sort = params.get('sort')
if sort:
sort_list = sort.split(',')
else:
sort_list = ['-id']
startIndex = (page_no - 1) * page_size
endIndex = startIndex + page_size
filters = {'is_delete':0}
if name:
filters['name__startswith'] = name
if project_id:
filters['project_id'] = project_id
rows = ProjectVersion.objects.filter(**filters).order_by(*sort_list)[startIndex:endIndex]
rows = ProjectVersionSerializer(rows, many=True).data
total = ProjectVersion.objects.filter(**filters).count()
result['msg'] = '获取成功'
result['success'] = True
result['data'] = {}
result['data']['rows'] = rows
result['data']['total'] = total
return Response(result, status.HTTP_200_OK)
except Exception as e:
result['msg'] = '%s' % e
result['success'] = False
return Response(result, status.HTTP_500_INTERNAL_SERVER_ERROR)
方法3-通过raw函数执行原生sql
以下是项目中的一个实例,和本文上述内容没有任何关联,关键部分背景已着色,笔者偷懒,不做过多解释了,简单说下下面这段代码对用途:
主要是实现类似以下查询,获取指定分页对数据以及满足条件的记录记录总数。
SELECT tb_project.*, project_name_associated, project_id_associated, platform FROM tb_project
LEFT JOIN tb_project_associated ON tb_project.id=tb_project_associated.project_id
ORDER BY id DESC
LIMIT 0,10
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from backend.models import Project
from backend.serializers import ProjectSerializer
import logging
logger = logging.getLogger('mylogger')
class ProjectListAPIView(APIView):
'''
项目视图-项目管理-项目列表
'''
# 查询列表数据
def get(self, request, format=None):
result = {}
try:
params = request.GET
page_size = int(params.get('pageSize'))
page_no = int(params.get('pageNo'))
name = params.get('name')
project_status = params.get('status')
sort = params.get('sort')
order_by = 'id desc'
if sort:
order_by = sort
startIndex = (page_no - 1) * page_size
where = 'WHERE tb_project.is_delete=0 '
filters = {'is_delete':0}
if name:
filters['name__startswith'] = name
where += 'AND locate("%s", name) ' % name
if project_status:
where += "AND status='%s'" % project_status
sql = 'SELECT tb_project.id, COUNT(1) AS count FROM tb_project LEFT JOIN tb_project_associated ON tb_project.id=tb_project_associated.project_id '
query_rows = Project.objects.raw(sql)
total = query_rows[0].__dict__.get('count') if query_rows else 0
sql = 'SELECT tb_project.*,project_name_associated, project_id_associated, platform FROM tb_project LEFT JOIN tb_project_associated ON tb_project.id=tb_project_associated.project_id ' \
'%s ORDER BY %s ' \
'LIMIT %s,%s ' % (where,order_by, startIndex, page_size)
query_rows = Project.objects.raw(sql)
rows = []
for item in query_rows:
item.__dict__.pop('_state')
item.__dict__['create_time'] = item.__dict__['create_time'].strftime('%Y-%m-%d %H:%M:%S')
item.__dict__['update_time'] = item.__dict__['update_time'].strftime('%Y-%m-%d %H:%M:%S')
item.__dict__['begin_time'] = item.__dict__['begin_time'].strftime('%Y-%m-%d')
item.__dict__['end_time'] = item.__dict__['end_time'].strftime('%Y-%m-%d')
rows.append(item.__dict__)
result['msg'] = '获取成功'
result['success'] = True
result['data'] = {}
result['data']['rows'] = rows
result['data']['total'] = total
return Response(result, status.HTTP_200_OK)
except Exception as e:
result['msg'] = '%s' % e
result['success'] = False
return Response(result, status.HTTP_500_INTERNAL_SERVER_ERROR)
参考链接
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet.extra
https://www.jianshu.com/p/973971880da7
Django 不通过外键实现多表关联查询的更多相关文章
- django模型中有外键关系的表删除相关设置
0904自我总结 django模型中有外键关系的表删除相关设置 一.一对一 例如有Author.AuthorDetail两表 author = models.OneToOneField(to='Aut ...
- mysql 外键约束及表关联
一.MYSQL中的约束 1.主键:primary key 唯一非空的特性并且可以优化查询速度 2.外键:foreign key 外键的作用保证2个或2个以上的数据表的数据一致性和完整性 3.唯一:un ...
- django模型中, 外键字段使用to_filed属性 指定到所关联主表的某个字段
在django项目的开发过程中,在设计模型时一开始将主键设置成了一个自定义的字段,但是在创建搜索索引时却发现必须要存在一个id的字段,并且为主键(不知道是否是项目一开始就这样配置的原因), 但此时表结 ...
- MySQL的外键,修改表,基本数据类型,表级别操作,其他(条件,通配符,分页,排序,分组,联合,连表操作)
MySQL的外键,修改表,基本数据类型,表级别操作,其他(条件,通配符,分页,排序,分组,联合,连表操作): a.创建2张表 create table userinfo(nid int not nul ...
- (4)MySQL的外键(不同表之间的数据关联)
问题:下列这张表中部门等列名下输入的数据没有约束,那么可以随便填写符合规则的数据但是不符合实际需求的值,这样就造成了不符合规则的数据在表中存在,外键就是为了解决这个问题,管理员可以在另一张表中设置好符 ...
- MySQL数据库(4)_MySQL数据库外键约束、表查询
一.外键约束 创建外键 --- 每一个班主任会对应多个学生 , 而每个学生只能对应一个班主任 ----主表 CREATE TABLE ClassCharger( id TINYINT PRIMARY ...
- 吃货眼中的sqlalchemy外键和连表查询
前言 使用数据库一个高效的操作是连表查询,一条查询语句能够查询到多个表的数据.在sqlalchem架构下的数据库连表查询更是十分方便.那么如何连表查询?以及数据库外键对连表查询有没有帮助呢?本篇文章就 ...
- MySQL数据库 外键,级联, 修改表的操作
1.外键: 用来建立两张表之间的关系 - 一对多 - 多对多 - 一对一 研究表与表之间的关系: 1.定义一张 员工部门表 id, name, gender, dep_name, dep_desc - ...
- SQLAlchemy03 /外键、连表关系
SQLAlchemy03 /外键.连表关系 目录 SQLAlchemy03 /外键.连表关系 1.外键 2.ORM关系以及一对多 3.一对一的关系 4.多对多的关系 5.ORM层面的删除数据 6.OR ...
- SQLAlchemy(三):外键、连表关系
SQLAlchemy03 /外键.连表关系 目录 SQLAlchemy03 /外键.连表关系 1.外键 2.ORM关系以及一对多 3.一对一的关系 4.多对多的关系 5.ORM层面的删除数据 6.OR ...
随机推荐
- GeneralUpdate .Net5 WPF、Winfrom、控制台应用自动更新组件
https://www.bilibili.com/video/BV1aX4y137dd/?vd_source=43d3e66cc2ad46bac54dfb0c6a3a0a23 GeneralUpdat ...
- 头条abogus与Js补环境代理Upgrade!
声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 目标网站 aHR0cHM6 ...
- 类的阐述 package(包)
类的阐述 同一个文件中可以定义很多类 编译后,每个类都会生成独立的.class文件 一个类中,只能有一个主函数,每个类都可以有自己的主函数 public修饰的类称为公开类,要求类名必须与文件名称完全相 ...
- git基础命令 gitHub
git 和 gitHub git : 本地项目版本管理工具 gitHub : 相当于一个有很多功能的百度云盘,存储本地项目版本,管 ...
- 讲课 PPT 公开啦
目前限于时间原因,只在 Github Pages 上托管了. 之后有时间会托管到 pythonanywhere 上,因为 Github Pages 是在太慢了.
- vitual box 安装centos7
vitual box下载地址: https://www.virtualbox.org/wiki/Downloads centos7下载地址: CentOS Linux 选择7-2009,x86-64 ...
- 如何将 iPhone 的照片同步到 windows 电脑上
首先在电脑上,新建一个文件夹,并把共享权限打开. 文件夹 右键 属性,共享,添加 Everyone. 然后,让手机和电脑连接到同一个局域网,手机热点即可. 在手机端看 文件 app,找到电脑的共享文件 ...
- 用 KV 缓存量化解锁长文本生成
很高兴和大家分享 Hugging Face 的一项新功能: KV 缓存量化 ,它能够把你的语言模型的速度提升到一个新水平. 太长不看版: KV 缓存量化可在最小化对生成质量的影响的条件下,减少 LLM ...
- AIGC底层技术介绍
1.AIGC概述 AIGC,全称Artificial Intelligence Generated Content,即人工智能生成内容.这是一种新兴的人工智能技术,其核心思想是利用人工智能模型,根据给 ...
- raksmart服务器部署SSL报错:ERR_CERT_COMMON_NAME_INVALID
背景:raksmart服务器 下篇 部署项目 SSL报错:ERR_CERT_COMMON_NAME_INVALID 安装certbot sudo apt update sudo apt install ...