一、queryset介绍
在ORM模型中我们查询到的数据类型中讲到queryset数据类型,本节将对其的主要特性进行讲解介绍。如bookList=Book.objects.all()查询到的bookList即为queryset对象。具体介绍如下:
1、支持切片
queryset对象支持且切片操作 ,但是不支持负数形式的切片。
print(bookList[1:])
2、支持遍历
for book in bookList: print(book.tittle)
3、属于惰性查询
查询集 是惰性执行的 —— 创建查询集不会带来任何数据库的访问,即bookList=Book.objects.all()并不会执行查询的相关sql语句,只有在用到数据库中的数据时才会进行数据库查询,如遍历,切片查询等。
4、缓存机制
每个查询集都包含一个缓存来最小化对数据库的访问。如下实例:两次循环打印的结果一样,且只查询一次数据库,中间新插入的数据并不会被查询到,便是典型的缓存机制,解决办法就是重新再建一个查询集。
bookList=Book.objects.all() for book in bookList: print(book.tittle) Book.objects.create(tittle="linux",......) for book in bookList: print(book.tittle)
在一个新创建的查询集中,缓存为空。首次对查询集进行求值 —— 同时发生数据库查询 ——Django 将保存查询的结果到查询集的缓存中并返回明确请求的结果,下来对该查询集 的再次求值将重用缓存的结果,如上例所示。一定要注意的是缓存是发生在同一个缓存集,若对查询集使用不当,差生不同的查询集,如下实例,实际两次差生不同的两个查询集,即对相同的数据查询执行两次,显然会增加你的数据库负担。
print([book.title for book in models.Book.objects.all()]) print([book.publishDate for book in models.Book.objects.all()])
5、优化缓存
缓存机制显然对于数据量很大的数据库的查询显然不是很合理,非常占用内存,所以对一些不需要一次性得到所有数据的操作可以使用相应的优化缓存机制处理,如下:
(1)exist()
如下下例,即使使用if语句进行判断,也会完全执行整个数据库查询,即使我们不需要所有的数据:
bookList=Book.objects.all() if bookList: print("有数据")
但是通过如下方式使用exist()来检查是否有数据,其实其不会将所有的数据查出来缓存,只会取一个值:
bookList=Book.objects.all().exist() if bookList: print("有数据")
(2)iterator()
当queryset非常巨大时,处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统 进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法 来获取数据,处理完数据就将其丢弃。
bookList=Book.objects.all().iterator() for book in bookList: print(book.tittle)
注意,此时的bookList是一个生成器,取一个删除一个,所以若再次对其循环取值时,是没有数据可以取到的。
二、中介模型
class Class(models.Model): #班级表 nid=models.IntegerField(primary_key=True) name=models.CharField(max_length=32) class Student(models.Model): #学生表 nid=models.IntegerField(primary_key=True) name=models.CharField(max_length=12) Class=models.ForeignKey("Class") #与班级表建立多对一关系 courses=models.ManyToManyField("Course",through="Student2Course") #通过中介模型建立学生与课程多对多关系 class Course(models.Model): #课程表 nid=models.IntegerField(primary_key=True) name=models.CharField(max_length=32) class Student2Course(models.Model): #多对多关系表 nid=models.IntegerField(primary_key=True) course=models.ForeignKey("Course") student=models.ForeignKey("Student") score=models.IntegerField()
如上建立的models表类,一个学生有多们课程,一个课程可以有多个学生选,所以二者属于多对多关系,在之前篇章models模型中我们介绍过,这种关系表并不需要我们自己建立,通过指定多对多关系后,django会帮我们建立包含两张表主键字段的关系表。但是当我们要求关系表不但只有二者的对应关系信息时,如上例中还需要有每门功课的成绩的时,这种关系表的建立我们就需要通过中介模型来完成。
如上例,通过through指定关系表Student2Course,然后关系表通过如上方式,我们自己进行建立,这便称为中介模型。既然关系表还包含其他字段,所以与普通的多对多字段不同,你不能使用add、 create和赋值语句(比如,beatles.members = [...])来创建关系。创建关系还是需要通过查询或创建models对象后,然后在关系表中创建关系。值得注意的是:remove()方法被禁用也是出于同样的原因。但是clear() 方法却是可用的。它可以清空某个实例所有的多对多关系。
三、extra()函数
有些情况下,Django的查询语法难以简单的表达复杂的 WHERE 子句,对于这种情况, Django 提供了 extra() QuerySet修改机制 — 它能在 QuerySet生成的SQL从句中注入新子句,此处主要介绍一下它的select参数。
select 参数可以让你在 SELECT 从句中添加其他字段信息,它应该是一个字典,存放着属性名到 SQL 从句的映射。简单点说extra与annotate分组函数的效果相似,都是给查询到的queryset增加新的查询结果字段,只是annotate是通过聚合函数对原有数据进行计算得到的结果,而extra则是通过where判断对原始数据进行判断得到结果,见如下实例:
annotate():
通过annotate对查询到queryset进行分组,注意分组的标准是:查询到每一条数据,以包含的所有字段为标准,若有一个对应字段不一样,则此条数据便独自为一组。如思路二查询的queryset对象只有publish_id字段,故分组时就以其分组了。相同的publish_id自然分到一组,通过count函数也就计算到它的书籍个数。
例:每一个出版社的名字与出版社的书籍的个数
思路一:
ret=Publish.objects.all().annotate(c=Count("book__id")).values("name","c") print(ret)
思路二:
ret=Book.objects.all().values("publish_id").annotate(c=Count("nid")).values("publish__name","c") print(ret)
extra()
如下为extra()函数中select参数应用实例,其结果就是对查询到的结果增加一个出版年月的字段,并通过values取到此字段。提醒:Book表中publishDate是以秒的形式存储的,当我们需要统计某一年或者某一年和月出版的书籍数量,我们可以通过此形式"strftime('%%Y-%%m',publishDate)"将其转换相应格式,其中strftime中第一个参数就是我们想要的时间格式。
ret=Book.objects.all().extra(select={"pub_year_month":"strftime('%%Y-%%m',publishDate)"}).values("title","pub_year_month") print(ret)
四、整体插入
假如需要我们一次性向Book表插入100条数据,我们该怎样做呢?也许我们会按照如下的方式进行(假设Book表就tittle和price两个字段):
for i in range(100): Book.objects.create(title="book"+str(i),price=i*4)
上述这样的方式是可以达到我们一次性插入多条数据,但是显然不合理,每插入一条数据则会调用一次数据库。因此我们可以通过bulk_create一次性插入这一百条数据,称为批量插入,它只会调用一次数据库,很好的解决了上述的问题,具体实例如下:
bookList=[] for i in range(100): book=Book(title="book"+str(i),price=i*4) bookList.append(book) Book.objects.bulk_create(bookList)
如上示例,我们可以看出bulk_create批量插入的数据必须是实例对象,且多个实例对象需要放在列表中。