Python——Django框架(五)


ORM多表操作之多对多

一、多对多添加记录

1、添加多对多关系

继续前面的。多一个作者(Author)表,一本书可以有多个作者,一个作者可以有多本书,这就是多对多的关系。

首先要知道,两张表是完成不了多对多的关系的。

那么多对多怎么实现呢?有个很通俗的单词:ManyToMany:

python怎样一对多 python一对多查询_数据库


我这作者的表已经创建过了,接着如果想要再加一个字段:

python怎样一对多 python一对多查询_数据库_02


也就是想要再加一个age字段,就会出现这种去情况:

python怎样一对多 python一对多查询_多对多_03


系统不知道你新加的这个 age 应该赋予什么值,所以我们可以给它一个null:

python怎样一对多 python一对多查询_数据库_04

接着输入代码创建表,这时就会发现,除了原本的三张表(Book,publish,author),还多了一张表:

python怎样一对多 python一对多查询_缓存_05


这个ManyToMany就会自动帮你创建第三张表。

现在表已经创建好了,就要绑定多对多的关系了。

python怎样一对多 python一对多查询_数据库_06


上面的数字,是手动填上去的。我们能不能通过ORM来给这张表添加信息呢?当然不行!!

现在是通过编辑器来直接对这张表操作的,但是如果回到用ORM来赋值,你会发现根本没有相关的代码。

所以我们只能通过对象的方式来绑定关系。

那么我们该怎么绑定呢?看下图:

python怎样一对多 python一对多查询_python怎样一对多_07

解释:首先要先取出这本书的对象,然后如果想要为这本书添加作者,就通过 add() 的方式来添加,当然,我们应该要先取出作者的信息:

python怎样一对多 python一对多查询_数据库_08

这里取出的是所有作者的信息,取出的是一个集合,所以下面add增加操作那里,要加个星号 * 。当然,如果取出的只有一个,就不用 * 了。看下图:

python怎样一对多 python一对多查询_python怎样一对多_09

这时候,就可以发现,多对多的绑定关系出来了:

python怎样一对多 python一对多查询_python怎样一对多_10

继续前面的,如果添加多个作者信息,就会这样:

python怎样一对多 python一对多查询_多对多_11


三个作者都对应了一本书了。

2、删除多对多关系

python怎样一对多 python一对多查询_多对多_12

这样,就解除了绑定的多对多的关系:

python怎样一对多 python一对多查询_缓存_13


可以看到,成功解除了多对多的关系。当然,还有相对简便的方法:

python怎样一对多 python一对多查询_python怎样一对多_14


这个操作意思是:把 id=3 的书,删除 id=3 的作者的多对多关系。如果删除不存在的 id ,也不会报错。

3、通过直接操作来赋予多对多关系(不推荐)

注意:

python怎样一对多 python一对多查询_python怎样一对多_15


这里把那个 ManyToMany 的字段删了,然后输入相关代码 (… migrate),系统会自动把这个字段相关联的数据给删掉。



前面都是通过对象的方式来赋予多对多关系。如果我们就是要直接操作来赋予多对多关系,那该怎么做呢?

答案是可以的。前面我们是通过ManyToMany 来让数据库自动帮我们创建第三张表。如果我们手动创建第三张表,就可以实现通过直接操作来赋予多对多关系。

记得前面讲过,实质上是没有多对多的关系的,是通过两张表的一对多,来实现多对多的关系。

python怎样一对多 python一对多查询_python怎样一对多_16

两个都是外键!
创建外键,系统会自动帮我们在后面加上 _id。

然后创建表:

python怎样一对多 python一对多查询_python_17

接着我们就开始赋予多对多关系:

python怎样一对多 python一对多查询_python_18

解释:绑定书id为2,作者id为3,的多对多关系。

可以看到,创建成功:

python怎样一对多 python一对多查询_python怎样一对多_19

二、多对多查询(接上)

然后开始关联查询:

python怎样一对多 python一对多查询_python_20


python怎样一对多 python一对多查询_python怎样一对多_21


可以看到,取出来了对象集合。接着取第一个对象,看它的信息:

python怎样一对多 python一对多查询_数据库_22


python怎样一对多 python一对多查询_python_23

可以看到,作者的信息出来了。

可以发现,这种操作跟一对多的差不多。学到这里,就可以忘记这种操作了,因为后面主要不是通过这种操作来赋予关系,主要是通过filter,双下划线等等的方式。但是这里要知道怎么回事,有这么一个方法,就行了。

还没结束,我们尝试查询 alex出过的书籍名称及价格:

python怎样一对多 python一对多查询_python_24

python怎样一对多 python一对多查询_数据库_25

python怎样一对多 python一对多查询_缓存_26


python怎样一对多 python一对多查询_缓存_27


可以看到,能查出来,但是因为这表是我们手动创建出来的,所以这么查起来比较麻烦,如果是ManyToMany,会轻松很多。



如果是ManyToMany,看下图:

python怎样一对多 python一对多查询_缓存_28


可以看到,最终是可以查到的(中间的数据改过,但重要的是能查出来):

python怎样一对多 python一对多查询_python怎样一对多_29

三、查询之聚合查询,分组查询

1、聚合查询

如果你想要例如 sub ,min 之类的函数,要先导入:

python怎样一对多 python一对多查询_多对多_30

来个需求:现在想要所有书的平均价格,怎么求?看下图:

python怎样一对多 python一对多查询_缓存_31

然后结果:

python怎样一对多 python一对多查询_缓存_32

如果是求和呢?

python怎样一对多 python一对多查询_缓存_33

结果:

python怎样一对多 python一对多查询_缓存_34

再来个需求,求 alex出过的书的总价格:

首先要筛选条件,接着再求和:

python怎样一对多 python一对多查询_多对多_35

结果:

python怎样一对多 python一对多查询_python_36


可以看到 这个 ‘price__sum’ 是系统自动帮我们做拼接的,我们也可以自己命名:

python怎样一对多 python一对多查询_python怎样一对多_37


结果:

python怎样一对多 python一对多查询_缓存_38

2、分组查询

顾名思义,我们可以根据某个条件来分组,这里用名字举例:

python怎样一对多 python一对多查询_数据库_39


先筛选作者姓名,再根据这个来分组:

python怎样一对多 python一对多查询_数据库_40


解释:此操作意思是每一个作者出过的书的总价格是多少。

结果:

python怎样一对多 python一对多查询_python怎样一对多_41


再来个需求,每一个出版社所出过最便宜的书的价格:

python怎样一对多 python一对多查询_多对多_42

结果:

python怎样一对多 python一对多查询_python_43

四、多表操作之F查询与Q查询

仅仅靠单一的关键字查询已经很难满足查询的要求。此时Django为我们提供了F查询和Q查询。

如果要使用F、Q查询,首先要引入它们:

python怎样一对多 python一对多查询_python_44

1、 F查询

来个需求:如果此时想要所有书的价格都提价10块钱,该怎么做?尝试下:

python怎样一对多 python一对多查询_多对多_45


可以看到,这种方式是不行的。

所以此时我们需要F、Q的帮助

python怎样一对多 python一对多查询_python_46


可以看到,这样子就没问题了。结果也确实都加了10块钱。

2、Q查询
a、

我们能做到“与”查询,但是“非”跟“或”做不到。所以这时就需要Q查询来帮助:

python怎样一对多 python一对多查询_多对多_47


通过Q包起来,就可以使用管道符 “ | ” ,来完成操作。

通过Q包起来,就可以在Q前面加波浪线 “ ~ ” ,来完成操作。举例子:

python怎样一对多 python一对多查询_多对多_48

输出价格为87或者名字为GO的书:

python怎样一对多 python一对多查询_python_49

输出名字不是GO的书:

python怎样一对多 python一对多查询_python怎样一对多_50


python怎样一对多 python一对多查询_python_51

b、模糊查询

如果要查找书的名字里带有G的,该怎么做呢?看下图:

python怎样一对多 python一对多查询_多对多_52


python怎样一对多 python一对多查询_数据库_53

3、F、Q查询与关键字查询组合使用

例子:

python怎样一对多 python一对多查询_python怎样一对多_54


结果:

python怎样一对多 python一对多查询_python怎样一对多_55

注意:如果要组合查询,F或Q查询,一定要放在关键字查询前面!

五、QuerySet集合对象的特性

QuerySet特点:
1、可迭代的
2、可切片

1、惰性求值

python怎样一对多 python一对多查询_python怎样一对多_56


当执行了上面这么一段代码的时候,数据库有没有查询?

答案是没有。

这个现象,是 querySet 造成的。这种现象称为“惰性求值”。

当你需要用到的时候,才会执行,否则不会执行。为什么要这么设计呢?
因为到数据库查询需要时间;如果你现在不用,后面再用,这样子就会浪费时间。

只要你用到,就会执行,比如:

python怎样一对多 python一对多查询_多对多_57

比如这种。

注意:

但是,这种情况就不一定了:

python怎样一对多 python一对多查询_数据库_58


两个 for 循环,虽然看起来是执行了两次,但实质上只执行了一次!

Django给 querySet 做了一个缓存,第一次遍历的时候,会存进缓存,第二次再用到的时候,就不会到数据库拿,而是进缓存里拿。这种情况,也能提升性能。

我们可以验证一下:

python怎样一对多 python一对多查询_数据库_59


结果:

python怎样一对多 python一对多查询_缓存_60


结论:确实是没有到数据库里拿,而是进到了缓存里。这就是利弊关系了。

但是真的没有办法吗?

很简单,你重新赋值就行了!

python怎样一对多 python一对多查询_缓存_61

2、关于exists与节省缓存

现在有这么个需求,我只要看下QuerySet里面有没有数据,但是不要它存到缓存里:
xxx.exists()

看下图:

python怎样一对多 python一对多查询_python_62

这么写,虽然还是会走数据库,但是不会把里面的数据存到缓存里面。

3、QuerySet与迭代器和生成器

如果数据很多,一下放到缓存里,对缓存也是个很大的消耗,我们可以用到以前学过的迭代器与生成器,一条一条放到缓存里,用到的时候才拿。

首先就要先做一个迭代器:

通过 iterator():

python怎样一对多 python一对多查询_数据库_63


迭代器的特点:只能遍历一次,遍历完就没有了,比如下图的两个for循环:

python怎样一对多 python一对多查询_python_64


只能输出一次,第二次已经没有了,所以输出不出来。总结:

python怎样一对多 python一对多查询_缓存_65