首先了解一下 mysql中的表之间的关系,一对一,一对多,多对一,多对多。

一对多关系、多对一关系、一对一关系

  1. 至少都有一侧是单个实体,所以记录之间的联系通过外键实现,让外键指向这个实体。
  2. 实现这种关系时,要在“多”这一侧加入一个外键,指向“一”这一侧联接的记录。

多对多关系

  1. 解决方法是添加第三个表,这个表称为关联表。
  2. 多对多关系可以分解成原表和关联表之间的两个一对多关系

多对多关系例子

查询多对多关系要分成两步。

  1. 若想知道某位学生选择了哪些课程,要先从学生和注册之间的一对多关系开始, 获取这位学生在 registrations 表中的所有记录。
  2. 然后再按照多到一的方向遍历课程和注册之间的一对多关系, 找到这位学生在 registrations 表中各记录所对应的课程。
  3. 同样,若想找到选择了某门课程的所有学生,你要先从课程表中开始,获取其在 registrations 表中的记录,再获取这些记录联接的学生。

自引用关系也是多对多的一种特殊情况

如果关系中的两侧都在同一个表中, 这种关系称为自引用关系。在关注中, 关系的左侧是用户实体,可以称为“关注者”;关系的右侧也是用户实体,但这些是“被关注者”。

创建一个多表之间关系的例子

实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
模型建立如下:

from django.db import models

# Create your models here.

class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField() # 与AuthorDetail建立一对一的关系
authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE) class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
birthday = models.DateField()
telephone = models.BigIntegerField()
addr = models.CharField(max_length=64) class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField() class Book(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
publishDate = models.DateField()
price = models.DecimalField(max_digits=5, decimal_places=2) # 与Publish建立一对多的关系,外键字段建立在多的一方
publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors = models.ManyToManyField(to='Author', )

添加记录

#方式一:如果是这样直接指定publish_id字段去添加值,前提是你的主表里面必须有数据
# 主表:没有被关联的(因为book表是要依赖于publish这个表的)也就是publish表
# 子表:关联的表
book_obj = Book.objects.create(title='小猪猪', publishDate='2012-12-12', price=200, publish_id=1)
#方式二 建议
pub_obj = Publish.objects.filter(name='西瓜出版社')[0])
Book.objects.create(title='神雕侠侣',publishDate='2013-12-12',price=188,publish=pub_obj)
#第二种方法可以避免出版社不存在的问题

多对多添加记录:

  书和作者是多对多的关系:一个书可以有多个作者,一个作者可以出版多本书

  步骤:先找到书对象

     再找到需要的作者对象

     给书对象绑定作者对象(用add方法),也就是绑定多对多的关系.

#方式一
#查找作者id
xiaojiu =Author.objects.filter(name='xiaojiu').first()
zhang = Author.objects.filter(name='zhang').first()
#给书籍绑定作者
book_obj.authors.add(zhang,xiaojiu)

#方式二

  pub_obj = Publish.objects.filter(name='苹果出版社').first()
  book_obj = Book.objects.create(title='苹果书',publishDate='2012-2-2',price=188,publish=pub_obj)
  authers = Author.objects.all()

  绑定多对多的关系
  book_obj.authors.add(*authers)

  

解除绑定:remove

 # 将某个特定的对象从被关联对象集合中去除。    ======   book_obj.authors.remove(*[])

    book_obj = Book.objects.filter(title='小猪猪5').last() #找到对象
authers = Author.objects.filter(nid__lt=3)#找到符合作者的对象
book_obj.authors.remove(*authers) #因为是多个所以要加*

清除绑定:clear”  #清空被关联对象集合。

  book_obj = Book.objects.filter(title='小猪猪4')#找到对象
for book_obj_item in book_obj: #循环赋值
book_obj_item.authors.clear()#清空关联对象

总结:remove和clear的区别
  remove:得吧你要清除的数据筛选出来,然后移除
  clear:不用查,直接就把数据都清空了。
  各有应用场景

 

一对一查询

author和authordetile是一对一的关系

正向查询(按字段author)

反向查询(按表名authordeital):因为是一对一的关系了,就不用_set了。

    #一对一查询
#正向查询: 手机号为110的作者姓名
deital_obj = AuthorDetail.objects.filter(telephone=110).first()
print(deital_obj.author.name) #反向查询:查询作者的手机号
xiaojiu_obj = Author.objects.filter(name='xiaojiu').first()
print(dir(xiaojiu_obj)) #如果找不到方法,用dir 看看这个对象都有什么方法
print(xiaojiu_obj.authorDetail.telephone)

一对多查询

正向查询(按字段:publish):

反向查询(按表名:book_set):

    #正向查询:查询 小红帽这本书的出版社地址
book_obj = Book.objects.filter(title='小红帽')[0] #拿到书对象
print('=======',book_obj.publish)
print(book_obj.publish.city) #反向查询:查询苹果出版社都初版过哪本书和价格
pub_obj = Publish.objects.filter(name='苹果出版社')[0]
book_dic = pub_obj.book_set.all().values('price','title')[0]
print(book_dic)
print(book_dic['price'])
#查询出版过的所有书籍
book_list = pub_obj.book_set.all()
for book_obj in book_list:
print(book_obj.title,book_obj.price)

庄杰大佬解答了我的疑惑, 在这里谢谢大佬,小弟甘拜下风

多对多查询

正向查询(按字段authorlist)

反向查询(按表名book_set)

    #多对多查询
#正向查询:查询小红帽这本书的所有作者的姓名和年龄
book_obj = Book.objects.filter(title='小猪猪3')[0]
print(book_obj.authors.all().values('name','age')) #反向查询 : 查询作者是xiaojiu的 出了那几本书
#方法一: 使用book_set.all xiaojiu_obj = Author.objects.filter(name='zhang').first()
alls= (xiaojiu_obj.book_set.all())
for i in alls:
print(i.title)
#方法二:使用values
author_list = xiaojiu_obj.book_set.all().values('title')
for i in author_list:
print(i.get('title'))

你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Article model 中做一下更改: publish = ForeignKey(Blog, related_name='bookList'),那么接下来就会如我们看到这般

# 查询 人民出版社出版过的所有书籍

   publish=Publish.objects.get(name="人民出版社")

   book_list=publish.bookList.all()  # 与人民出版社关联的所有书籍对象集合

基于双下划线的查询

Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。(相当于用sql语句用join连接的方式,可以在settings里面设置,可查看sql语句)

一对多查询

#练习1、苹果出版社出版过的所有的书的价格和名字
    #基于双下划线的查询方式
#第一种查法
ret = Publish.objects.filter(name='苹果出版社').values('book__title','book__price')
# #先找到苹果出版社这个对象,之后再通过vlaues取值,book表下的什么字段中间用__链接,固定写法
print(ret)
#第二种查法
#对比第一种方法,这种方法就是先join publish表,之后再进行取值。
ret2 = Book.objects.filter(publish__name='苹果出版社').values('title','price')
print(ret2)
  #练习2、查询手机号以11开头的作者出版过的所有书的名称以及出版社的名称
    #方式1
author_obj = AuthorDetail.objects.filter(telephone__startswith='11').first()#找到以11开头的电话号,获取AuthorDetail 主键对象 print(author_obj.author.book_set.all().values('title','publish__name'))#获取该条件所对应的作者,获取所有书籍对象信息,通过values取值。
#方式2 ret =Book.objects.filter(authors__authorDetail__telephone__startswith='11').values('title','publish__name')
print(ret)

聚合查询与分组查询(很重要!!!)

聚合查询:aggregate(*args, **kwargs),只对一个组进行聚合

#查询 所有书籍价格的平均值
    print(Book.objects.all().aggregate(Avg('price')))

aggregate()QuerySet 的一个终止子句(也就是返回的不再是一个QuerySet集合的时候),意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

    print(Book.objects.all().aggregate(avgprice =Avg('price')))

如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

print(Book.objects.all().aggregate(Avg('price'),Max("price"),Min("price")))

#输出
{'price__avg': 186.222222, 'price__max': Decimal('200.00'), 'price__min': Decimal('100.00')}

分组查询 :

annotate():为QuerySet中每一个对象都生成一个独立的汇总值。

是对分组完之后的结果进行的聚合

    #统计每一本书的作者数量
#方式一
print(Book.objects.all().annotate(authorNum = Count('authors__name')).values('authorNum'))
#方式二
book_list = Book.objects.all().annotate(authorNum = Count('authors__name'))
for book_obj in book_list:
print(book_obj.title,book_obj.authorNum)

#统计每一个出版社最便宜的书

  #方式一
print(Book.objects.values('publish__name').annotate(MinPrice=Min('price'))) #方式二
print(Publish.objects.all().annotate(minprice=Min('book__price')).values('name','minprice')) #方式三
publishlist = Publish.objects.annotate(minprice =Min('book__price'))
for pub_obj in publishlist:
print(pub_obj.name,pub_obj.minprice)

#打印以py开头的书籍对应的作者个数

   print(Book.objects.filter(title__startswith='py').annotate(au_count = Count('authors__name')).values('au_count'))
#根据一本书的作者数量多少对查询集QuuerySet进行排序
    print(Book.objects.all().annotate(Num=Count('authors__name')).order_by('Num').values('title'))
  #获取book所有对象,创建分组Num 统计作者的数量,通过order_by进行排序, values取值

#查询各个作者出的书的总价格

#方式一
print(Author.objects.all().annotate(pricesum = Sum('book__price')).values('name','pricesum'))
#方式二
print(Book.objects.values('authors__name').annotate(pricesum = Sum('price')).values('authors__name','pricesum'))

F与Q查询

F查询:

在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

#查询书籍价格小于100元并且作者年龄=2017的书

print(Book.objects.filter(Q(price__lt='100')&Q(authors__authorDetail__birthday__year=2017)).values('title'))

#修改也可以使用F函数,比如说 书籍ID为4的书籍价格上涨100元

 print(Book.objects.filter(nid=4).update(price=F('price')+100))

#Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

#查询书记价格小于作者年龄*2的书籍

    print(Book.objects.filter(price__lt=F('authors__age')*2).values('title'))

Q查询:

filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象

# 查询书籍价格小于100元并且作者年份等于2017的书

print(Book.objects.filter(Q(price__lt='100')&Q(authors__authorDetail__birthday__year=2017)).values('title'))

#查询评论数大于100或者阅读数小于200的书

print(models.Book.objects.filter(Q(commentNum__gt=100)|Q(readNum__lt=200)))
Q 对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。

#查询年份等于2017年或者价格大于200的书

 print(models.Book.objects.filter(Q(publishDdata__year=2017)|Q(price__gt=200)))

#查询年份不是2017年或者价格大于200的书

print(models.Book.objects.filter(~Q(publishDdata__year=2017)&Q(price__gt=200)))

与:&Q

或:|Q

非:~Q

注意:

  bookList=models.Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
title__icontains="python"
)

查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:

Django(多表查询操作)的更多相关文章

  1. Django的model查询操作 与 查询性能优化

    Django的model查询操作 与 查询性能优化 1 如何 在做ORM查询时 查看SQl的执行情况 (1) 最底层的 django.db.connection 在 django shell 中使用 ...

  2. 在MyBatis中查询数据、涉及多参数的数据访问操作、插入数据时获取数据自增长的id、关联表查询操作、动态SQL、关于配置MyBatis映射没有代码提示的解决方案

    1. 单元测试 在单元测试中,每个测试方法都需要执行相同的前置代码和后置代码,则可以自定义2个方法,分别在这2个方法中执行前置代码和后置代码,并为这2个方法添加@Before和@After注解,然后, ...

  3. MySQL/MariaDB数据库的多表查询操作

    MySQL/MariaDB数据库的多表查询操作 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.单表查询小试牛刀 [root@node105.yinzhengjie.org.cn ...

  4. Django多表查询

    一.前言 1.什么是ORM? ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候, ...

  5. Django 多表查询

    多表查询是模型层的重要功能之一, Django提供了一套基于关联字段独特的解决方案. ForeignKey 来自Django官方文档的模型示例: from django.db import model ...

  6. Django --- ORM表查询

    目录 使用数据库之前的配置工作 单表操作常用的方法 一对多字段的增删改查 多对多字段数据的增删改查 跨表查询 聚合函数 分组查询 F与Q查询 使用数据库之前的配置工作 settings.py中的配置 ...

  7. Django单表查询及其方法

    单表查询 前期准备 首先新建一个test的python文件,然后再manage.py中导入main语句及其下面的复制到新文件中 并导入django 写上django.setup() 就可以导入对应的m ...

  8. Django之表高级操作

    目录 一.如何开启自己的测试脚本? 二.对表数据的添加.更新.删除 1.create() 2.update() 3.delete() 4.查看执行的sql语句 三. 单表查询13个操作 返回Query ...

  9. Djano之ORM多表查询操作

    # 把 model 转化为 迭代器去循环 MODEL.objects.all().iterator() # 等同于 values, values_list, 但是 only 这种方式 获取字段属性依旧 ...

  10. Django之ORM查询操作详解

    浏览目录 一般操作 ForeignKey操作 ManyToManyField 聚合查询 分组查询 F查询和Q查询 事务 Django终端打印SQL语句 在Python脚本中调用Django环境 其他操 ...

随机推荐

  1. Docker之1---介绍和安装

    Docker介绍 Docker是一个开源项目,让应用程序布署在软件货柜下的工作可以自动化进行,借此在Linux操作系统上,提供一个额外的软件抽象层,以及操作系统层虚拟化的自动管理机制. Docker利 ...

  2. C++模板元编程----堆排序

    目录 目录 前言 实现的一些小细节 Debug 惰性求值 总结 Ref 前言 经过前两次经验的积累,终于来到了麻烦的堆排序.在一开始接触模板元编程的时候,我就期望有一天能够写出元编程堆排序的代码.原因 ...

  3. 用漫画的形式展现——URL和HTTP

    http请求内容:请求头:get.post等发送请求(其他:head.put.delete.option) host 地址 user-Agent cookie 通行证 head:与get请求类似,不同 ...

  4. 第十六章节 BJROBOT 开机自启动服务【ROS全开源阿克曼转向智能网联无人驾驶车】

    1.把小车平放在地板上,用资料里的虚拟机,打开一个终端 ssh 过去主控端运行rosrun robot_upstart install znjrobot/launch/bringup.launch 2 ...

  5. QT串口通信编程

    QT串口编程 文件夹目录结构如下图所示 设计的示例界面如下图所示 首先在项目文件里面添加一句 QT += serialport SerialPortDemo.pro文件如下: #----------- ...

  6. Head First 设计模式 —— 07. 适配器模式

    思考题 你能想到真实世界中,还有哪些适配器的例子? P236 HDMI 转 VGA 转换器 Type-C 转 3.5mm 线 适配器模式解析 客户使用适配器的过程: P241 客户通过目标接口调用适配 ...

  7. 《Spring Boot 实战纪实》之前言

    目录 前言 (思维篇)人人都是产品经理 1.需求文档 1.1 需求管理 1.2 如何攥写需求文档 1.3 需求关键点文档 2 原型设计 2.1 缺失的逻辑 2.2 让想法跃然纸上 3 开发设计文档 3 ...

  8. ACL技术(访问控制列表)

    • Access Control List    • 访问控制列表    • 是一种包过滤技术    • ACL基于IP包头的IP地址.四层TCP/UDP头部的端口号.[五层数据]进行过滤    • ...

  9. 记一次使用Asp.Net Core WebApi 5.0+Dapper+Mysql+Redis+Docker的开发过程

    #前言 我可能有三年没怎么碰C#了,目前的工作是在全职搞前端,最近有时间抽空看了一下Asp.net Core,Core版本号都到了5.0了,也越来越好用了,下面将记录一下这几天以来使用Asp.Net ...

  10. 借助Docker搭建JMeter+Grafana+Influxdb监控平台

    我们都知道Jmeter提供了原生的结果查看,既然有原生的查看结果,为什么还要多此一举使用其他工具进行查看呢,除了查看内容丰富外还有最主要的原因:Jmeter提供的查看结果插件本身是比较消耗性能的,所以 ...