核心知识点:

1.明白表之间的关系

2.根据关联字段确定正反向,选择一种方式

在Django的ORM种,查询既可以通过查询的方向分为正向查询和反向查询,也可以通过不同的对象分为对象查询和Queryset查询。

下面会一一进行讲解,首先给出表结构对应关系,以及表格中的数据:

from django.db import models

# Create your models here.

class Book(models.Model):
title = models.CharField(max_length=)
publish_date = models.DateField(auto_now_add=True)
price = models.DecimalField(max_digits=, decimal_places=)
# 创建外键,关联publish,一个出版社有多本书,书和出版社是多对一
publisher = models.ForeignKey(to="Publisher")
# 创建多对多关联author
#一本书可能有多个作者,一个作者可能有多本书,因此双方是多对多的关系
#因此会自动生成author_book表
#定义关系在book或者author任意一张表中指定都可以
#但是一般放在字段稍微少的表格中这样有利于数据查询
author = models.ManyToManyField(to="Author") def __str__(self):
return self.title # 出版社
class Publisher(models.Model):
name = models.CharField(max_length=)
city = models.CharField(max_length=) def __str__(self):
return self.name # 作者
class Author(models.Model):
name = models.CharField(max_length=)
age = models.IntegerField()
phone = models.IntegerField()
#作者和详情表是一对一关联
detail = models.OneToOneField(to="AuthorDetail") def __str__(self):
return self.name # 作者详情
class AuthorDetail(models.Model):
addr = models.CharField(max_length=)
email = models.EmailField()

表结构对应关系

mysql> show tables;
+----------------------------+
| Tables_in_books |
+----------------------------+
| app01_author |
| app01_authordetail |
| app01_book |
| app01_book_author |
| app01_publisher |
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+----------------------------+
15 rows in set (0.00 sec) mysql> select * from author;
ERROR 1146 (42S02): Table 'books.author' doesn't exist
mysql> select * from app01_author;
+----+-----------+-----+--------+-----------+
| id | name | age | phone | detail_id |
+----+-----------+-----+--------+-----------+
| 6 | 路遥 | 78 | 12 | 1 |
| 7 | 黄仁宇 | 67 | 12345 | 2 |
| 8 | 陈忠实 | 55 | 789 | 3 |
| 9 | 刘天斯 | 35 | 567 | 4 |
| 10 | 老男孩 | 40 | 456 | 5 |
| 11 | 北岛 | 56 | 123 | 6 |
| 12 | 付磊 | 35 | 56788 | 7 |
| 13 | 张益军 | 38 | 234566 | 8 |
+----+-----------+-----+--------+-----------+
8 rows in set (0.00 sec) mysql> select * from app01_publisher;
+----+-----------------------+--------+
| id | name | city |
+----+-----------------------+--------+
| 1 | 机械工业出版社 | 杭州 |
| 2 | 电子工业出版社 | 北京 |
| 3 | 长江文艺出版社 | 武汉 |
| 4 | 人民出版社 | 北京 |
| 5 | 岳麓书社出版社 | 扬州 |
+----+-----------------------+--------+
5 rows in set (0.00 sec) mysql> select * from app01_authordetail;
+----+--------------+-------+
| id | addr | email |
+----+--------------+-------+
| 1 | 陕西平原 | 1@ |
| 2 | 美国 | 2@ |
| 3 | 重庆 | 3@ |
| 4 | 江苏扬州 | 4@ |
| 5 | 北京 | 5@ |
| 6 | 湖北宜昌 | 6@ |
| 7 | 江苏 | 7@ |
| 8 | 安徽芜湖 | 8@ |
+----+--------------+-------+
8 rows in set (0.00 sec) mysql> select * from app01_book;
+----+----------------------------+--------------+-------+--------------+
| id | title | publish_date | price | publisher_id |
+----+----------------------------+--------------+-------+--------------+
| 1 | 平凡的世界 | 2009-04-22 | 56.00 | 4 |
| 2 | 跟老男孩学shell脚本 | 2012-02-16 | 78.00 | 2 |
| 3 | 万历十五年 | 2004-04-21 | 39.90 | 3 |
| 4 | python自动化运维 | 2017-01-10 | 68.00 | 1 |
| 5 | 中国大历史 | 2014-01-14 | 45.00 | 5 |
| 6 | 北岛诗集 | 1999-10-10 | 29.90 | 3 |
| 7 | Redis运维与开发 | 2019-10-01 | 68.00 | 1 |
+----+----------------------------+--------------+-------+--------------+
7 rows in set (0.00 sec) mysql> select * from app01_book_author;
+----+---------+-----------+
| id | book_id | author_id |
+----+---------+-----------+
| 1 | 1 | 6 |
| 2 | 2 | 10 |
| 3 | 3 | 7 |
| 4 | 4 | 9 |
| 5 | 5 | 7 |
| 6 | 6 | 11 |
| 7 | 7 | 12 |
| 8 | 7 | 13 |
+----+---------+-----------+
8 rows in set (0.00 sec)

表格中的相关数据

下面使用表格展示关系:

不管是基于对象查询,还是基于Queryset查询,都遵循正向查询字段,反向查询通过表名的规则。

关于何时是正向查,何时是反查取决于关联字段定义在那个列表上,如果字段在本地,查询关联字段就是正向查,反之则是反向查询。

关于格式上的用法:

一、基本查询

1.一对一(Author和Authordetail

(1)正向查询:查询黄仁宇的所在地址

可以看出,需要跨表,下面使用两种方式查询。

首先通过对象正向查:

models.Author.objects.filter(name="黄仁宇").first().detail.addr
'美国' #首先作者查信息,从作者查,所以是正向查询
#字段定义在谁那里,谁就是正的一方
#首先找到黄仁宇这个对象
models.Author.objects.filter(name="黄仁宇").first()
#然后通过detail这个字段直接查询相关联的对象
models.Author.objects.filter(name="黄仁宇").first().detail

通过Queryset对象正向查:

#正向查询按找字段名
models.Author.objects.filter(name="黄仁宇").values("detail__addr").first()
{'detail__addr': '美国'}
#这样也是可以得到相应的结果 #为什么说是基于queryset对象正向查询了?
models.Author.objects.filter(name="黄仁宇")
<QuerySet [<Author: 黄仁宇>]> #这是一个对象 #当然返回的结果也是对象
models.Author.objects.filter(name="黄仁宇").values("detail__addr")
<QuerySet [{'detail__addr': '美国'}]> #values其实就是帮你做了一个循环,按照你的要求得到相应的字段值

(2)反向查询:查询住在北京的作家的名称

分析:有住址的是一张表,有作家姓名的是一张表,所以要跨表。

基于对象查询:

#反向查询按照表名(小写)
models.AuthorDetail.objects.filter(addr="北京").first().author.name
'老男孩' #先找到住在北京的对象
models.AuthorDetail.objects.filter(addr="北京").first() #然后通过表名author直接查询,得到的也是对象

基于Queryset查询:

#反向查询通过表名,而且是基于Queryset
models.AuthorDetail.objects.filter(addr="北京").values("author__name").first()
{'author__name': '老男孩'} #Queryset对象返回的也是Queryset对象,values只是帮你循环取值,不会帮你转换数据类型

2.多对一(Book与publisher)

(1)正向查询:打印《万历十五年》这本书出版社的名字

分析:正向查询依然按照字段名publisher

基于对象查询:

#找到书这个对象
models.Book.objects.filter(title="万历十五年").first()
<Book: 万历十五年> #找到与《万历十五年》这本书先关联的出版社的名字
models.Book.objects.filter(title="万历十五年").first().publisher
<Publisher: 长江文艺出版社> #找到对象之后直接获取相应的属性
models.Book.objects.filter(title="万历十五年").first().publisher.name
'长江文艺出版社'

基于Queryset查询:

#找到相应的Queryset
models.Book.objects.filter(title="万历十五年").values("publisher__name")
<QuerySet [{'publisher__name': '长江文艺出版社'}]> #得到相应的字段值
models.Book.objects.filter(title="万历十五年").values("publisher__name").first()
{'publisher__name': '长江文艺出版社'} #为什么只有Queryset才能使用__下划线,因为values是专门正对于Queryset的方法

(2)反向查询

反向查询通过小写表名__set

基于对象查询:

models.Publisher.objects.filter(name="长江文艺出版社").first().book_set.values()
<QuerySet [{'id': 3, 'title': '万历十五年', 'publish_date': datetime.date(2004, 4, 21), 'price': Decimal('39.90'), 'publisher_id': 3}, {'id': 6, 'title': '北岛诗集', 'publish_date': datetime.date(1999, 10, 10), 'price': Decimal('29.90'), 'publisher_id': 3}]>

基于Queryset对象查询:

models.Publisher.objects.filter(name="长江文艺出版社").values("book__title")
<QuerySet [{'book__title': '万历十五年'}, {'book__title': '北岛诗集'}]>

3.多对多

(1)正向查询:Redis运维与开发所有的作者的名字

基于对象查询:

#找到相对应的对象
models.Book.objects.filter(title="Redis运维与开发")
<QuerySet [<Book: Redis运维与开发>]> #找到对象相关联的作者
models.Book.objects.filter(title="Redis运维与开发").first().author.values()
<QuerySet [{'id': 12, 'name': '付磊', 'age': 35, 'phone': 56788, 'detail_id': 7}, {'id': 13, 'name': '张益军', 'age': 38, 'phone': 234566, 'detail_id': 8}]> #取出相应的字段值
models.Book.objects.filter(title="Redis运维与开发").first().author.values("name",)
<QuerySet [{'name': '付磊'}, {'name': '张益军'}]>

基于Queryset查询

#找到Queryset对象
models.Book.objects.filter(title="Redis运维与开发")
<QuerySet [<Book: Redis运维与开发>]> #直接取相应的字段
models.Book.objects.filter(title="Redis运维与开发").values("author__name")
<QuerySet [{'author__name': '付磊'}, {'author__name': '张益军'}]>

(2)反向查询:黄仁宇出的所有书的名字, author——》book,属于多对多,字段定义在book,属于反向查询

基于对象查询:

#获取黄仁宇这个任务对象
models.Author.objects.filter(name="黄仁宇").first()
<Author: 黄仁宇> #通过表名+set获取相关联的书籍名称
models.Author.objects.filter(name="黄仁宇").first().book_set
<django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager object at 0x0000022D330E73C8> #获取相应的属性值
models.Author.objects.filter(name="黄仁宇").first().book_set.values("title")
<QuerySet [{'title': '万历十五年'}, {'title': '中国大历史'}]>

基于Queryset查询:

#获取黄仁宇这个Queryset
models.Author.objects.filter(name="黄仁宇")
<QuerySet [<Author: 黄仁宇>]> #直接通过values特性获取相应的信息
models.Author.objects.filter(name="黄仁宇").values("book__title")
<QuerySet [{'book__title': '万历十五年'}, {'book__title': '中国大历史'}]>

还有在过滤元素的时候,还可以通过__可以跨表进行参数设置:

#书找书
models.Book.objects.filter(author__name="黄仁宇").values("title")
<QuerySet [{'title': '万历十五年'}, {'title': '中国大历史'}]>
#可以看到与刚才基于对象和Queryset跨表结果一直 #作者找书
models.Author.objects.filter(name="黄仁宇").values("book__title")
<QuerySet [{'book__title': '万历十五年'}, {'book__title': '中国大历史'}]>

直接对着所需的对象,然后通过下换线过滤这种方法来的更加直白。

拓展:查询“机械工业出版社”出版的书籍名和书籍的作者姓名

#首先来查看需求
#出版社一个表、书籍一个表以及作者一个表,也就是有三个表 #正向查
models.Book.objects.filter(publisher__name="机械工业出版社").values_list("title","author__name")
<QuerySet [('python自动化运维', '刘天斯'), ('Redis运维与开发', '付磊'), ('Redis运维与开发', '张益军')]>
#联合book--publisher,找到书名
#联合book--author,再找到作者名,
#与上一题相比,就实在显示的时候多了一个__跨表差 #反向查
models.Publisher.objects.get(name="机械工业出版社").book_set.all().values_list("title","author__name")
<QuerySet [('python自动化运维', '刘天斯'), ('Redis运维与开发', '付磊'), ('Redis运维与开发', '张益军')]>
#publisher—(正向)—》book—(反向)—》author #双下划线反向查
models.Publisher.objects.filter(name="机械工业出版社").values_list("book__title","book__author__name")
<QuerySet [('python自动化运维', '刘天斯'), ('Redis运维与开发', '付磊'), ('Redis运维与开发', '张益军')]> #最后显示的时候一次性跨了两张表

二、聚合查询和分组查询

1.聚合

aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。

from django.db.models import Avg
models.Book.objects.all().aggregate(Avg("price"))
{'price__avg': 54.971429}

如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

models.Book.objects.all().aggregate(average_price=Avg("price"))
{'average_price': 54.971429}

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

models.Book.objects.all().aggregate(Avg("price"),Max("price"),Min("price"))
{'price__avg': 54.971429, 'price__max': Decimal('78.00'), 'price__min': Decimal('29.90')}

2.分组

为调用的QuerySet中每一个对象都生成一个独立的统计值

示例1:统计每一本书的作者个数

from django.db.models import Avg,Max,Min,Count
book_list = models.Book.objects.all().annotate(author_num=Count("author"))
for obj in book_list:
print(obj.author_num) 1
1
1
1
1
1

示例2:统计出每个出版社买的最便宜的书的价格

publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
for obj in publisher_list:
print(obj.min_price) 68.00
78.00
29.90
56.00
45.00

方法二:

models.Book.objects.values("publisher__name").annotate(min_price=Min("price"))
<QuerySet [{'publisher__name': '机械工业出版社', 'min_price': Decimal('68.00')}, {'publisher__name': '电子工业出版社', 'min_price': Decimal('78.00')}, {'publisher__name': '长江文艺出版社', 'min_price': Decimal('29.90')}, {'publisher__name': '人民出版社', 'min_price': Decimal('56.00')}, {'publisher__name': '岳麓书社出版社', 'min_price': Decimal('45.00')}]>

示例3:统计不止一个作者的图书

models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)
<QuerySet [<Book: Redis运维与开发>]>

示例4:根据一本图书作者数量的多少对查询集 QuerySet进行排序

models.Book.objects.annotate(author_num=Count("author")).order_by("author_num")
<QuerySet [<Book: 平凡的世界>, <Book: 万历十五年>, <Book: 中国大历史>, <Book: 跟老男孩学shell脚本>, <Book: python自动化运维>, <Book: 北岛诗集>, <Book: Redis运维与开发>]>

示例5:查询各个作者出的书的总价格

models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")
<QuerySet [{'name': '路遥', 'sum_price': Decimal('56.00')}, {'name': '黄仁宇', 'sum_price': Decimal('84.90')}, {'name': '陈忠实', 'sum_price': None}, {'name': '刘天斯', 'sum_price': Decimal('68.00')}, {'name': '老男孩', 'sum_price': Decimal('78.00')}, {'name': '北岛', 'sum_price': Decimal('29.90')}, {'name': '付磊', 'sum_price': Decimal('68.00')}, {'name': '张益军', 'sum_price': Decimal('68.00')}]>

三、F查询和Q查询

1.F查询

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

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

示例1:

查询评论数大于收藏数的书籍

from django.db.models import F
models.Book.objects.filter(commnet_num__lt=F('keep_num'))

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

models.Book.objects.filter(commnet_num__lt=F('keep_num')*2)

修改操作也可以使用F函数,比如将每一本书的价格提高30元

models.Book.objects.all().update(price=F("price")+30)

引申:

如果要修改char字段咋办?

如:把所有书名后面加上(第一版)

>>> from django.db.models.functions import Concat
>>> from django.db.models import Value
>>> models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"), Value(")")))

2.Q查询

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

示例1:

查询作者名是黄仁宇或老男孩

from django.db.models import Q
models.Book.objects.filter(Q(author__name="黄仁宇")|Q(author__name="老男孩"))
<QuerySet [<Book: 跟老男孩学shell脚本>, <Book: 万历十五年>, <Book: 中国大历史>]>

你可以组合& 和|  操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。

示例2:

查询作者名字是黄仁宇并且不是2014年出版的书的书名

models.Book.objects.filter(Q(author__name="黄仁宇") & ~Q(publish_date__year=2014)).values_list("title")
<QuerySet [('万历十五年',)]>

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

示例3:

查询出版年份是2017或2018,书名中带物语的所有书。

models.Book.objects.filter(Q(publish_date__year=2004) | Q(publish_date__year=2014), title__icontains="历")
<QuerySet [<Book: 万历十五年>, <Book: 中国大历史>]>

Django模型系统——ORM中跨表、聚合、分组、F、Q的更多相关文章

  1. Django模型系统——ORM

    一.概论 1.ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. 简单的说,ORM是通过使用描 ...

  2. Django模型系统——ORM校园管理系统代码

    1.models.py from django.db import models # Create your models here. class Class(models.Model): id = ...

  3. Django模型系统——ORM表结构对应关系

    对于数据库来说一般表结构只会有三种对应关系,分别是一对一.一对多和多对一,下面分别介绍: 1.一对多 何为一对多,例如一个学生只可能有一个班级,一个班级却又多个学生,班级表和学生表就是一对多的关系. ...

  4. {django模型层(二)多表操作}一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询、分组查询、F查询和Q查询

    Django基础五之django模型层(二)多表操作 本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 xxx 七 ...

  5. django模型系统(二)

    django模型系统(二) 常用查询 每一个django模型类,都有一个默认的管理器,objects QuerySet表示数据库中对象的列表.他可以有0到国歌过滤器.过滤器通过给定参数,缩小查询范围( ...

  6. Django模型层之单表操作

    Django模型层之单表操作 一 .ORM简介 我们在使用Django框架开发web应用的过程中,不可避免地会涉及到数据的管理操作(如增.删.改.查),而一旦谈到数据的管理操作,就需要用到数据库管理软 ...

  7. day 70 Django基础五之django模型层(二)多表操作

    Django基础五之django模型层(二)多表操作   本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 ORM ...

  8. day 56 Django基础五之django模型层(二)多表操作

    Django基础五之django模型层(二)多表操作   本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 ORM ...

  9. django模型系统(一)

    django模型系统(一) djangode ORM ORM:对像关系映射 用python概念去表达数据库 数据库配置(mysql) 安装pumysql 修改项目目录下的__init__.py imp ...

随机推荐

  1. vi 替换操作

    举例一: ,$s/// 从第一行到最后一行 147都替换为150 举例二: 例:替换当前文件中所有old为new :%s/old/new/g #%表示替换说有行,g表示替换一行中所有匹配点. 举例三: ...

  2. iOS仿支付宝首页效果

    代码地址如下:http://www.demodashi.com/demo/12776.html 首先看一下效果 状态栏红色是因为使用手机录屏的原因. 1.问题分析 1.导航栏A有两组控件,随着tabl ...

  3. Android应用程序开发以及背后的设计思想深度剖析

    1 http://www.uml.org.cn/mobiledev/201211063.asp 2 ...

  4. 摘录 LDAP

    1.LDAP就是 light DAP, 轻量级目录访问协议     LDAP是轻量目录访问协议(Lightweight Directory Access Protocol)的缩写     LDAP标准 ...

  5. Android布局中 android:layout_gravity="bottom"为何不起作用?

    在android布局时我们有时会需要将位于LinearLayout布局中的控件放在布局底部,或者是同时想将几个控件底部对齐,此时我们自然会想到使用 android:layout_gravity=&qu ...

  6. 【COCOS2DX-LUA 脚本开发之一】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!

    [COCOS2DX-LUA 脚本开发之一]在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途! 分类: [Cocos2dx Lua 脚本开发 ] 2012-04-1 ...

  7. 在Linux中配置DNS的正确方式

    链接:http://ccl.cse.nd.edu/operations/condor/hostname.shtml Common Hostname Problem on Linux Newly ins ...

  8. EJB是什么?(节选)

    近期的项目中使用了EJB.当时就仅仅知道怎么用,没有深入的去理解.当完毕这个项目之后.再回想项目中使用的技术.框架以及工具的时候,突然感觉对EJB这个概念非常是模糊,于是上网搜一些资料.可是,非常多的 ...

  9. python基础-------python2.7教程学习【廖雪峰版】(二)

    2017年6月7日14:59:27任务:    看完python基础1.计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值.但是,计算机能处理的远不止数值,还可以处理文 ...

  10. Swift开发教程--怎样设置状态栏的文字颜色

    第一步:在Info.plist中设置UIViewControllerBasedStatusBarAppearance 为NO 第二步:在viewDidLoad中加一句 UIApplication.sh ...