• Count()---统计一个字段的数量 # 标记符号
注意:Sum()---统计字段的值
示例:
def index(request):
# 统计book模型有多少本书(即有多少个id)
# distinct=True表示过滤掉重复项,默认值为False
count_ids=Book.objects.aggregate(book_count=Count('id',distinct=True)) # 返回整数,故不必再进行遍历查询
print(count_ids) # {'book_count': 4}
print(connection.queries)
return HttpResponse('index')
现在对distinct参数进行示例演示:
故意是两个id的email字段相同
def index2(request):
count_num_email=Author.objects.aggregate(num_email=Count('email',distinct=True))
print(count_num_email) # {'num_email': 3}
print(connection.queries)
'''
'sql': 'SELECT COUNT(DISTINCT author
.email
) AS num_email
FROM author
'
'''
return HttpResponse('index2')
注意,id有4个,这里去除重复项,所以返回3个,但是在数据库中,重复项是不会被删除的,请注意.
• 实现需求,获取每一本数的销量---无非就是统计外键表'book_id'分组的个数
示例:
def index2(request):
book_numbers=Book.objects.annotate(count_numbers=Count('bookorder'))
#print(book_numbers) # 这么处理获取的只是object对象
for book_number in book_numbers:
# 注意输出的count_numbers属性,才是想要的结果
print(book_number.name,book_number.count_numbers)
'''
西游记 0
三国演义 3
红楼梦 3
水浒传 0
'''
print(connection.queries)
return HttpResponse('index2')
小结:如果是aggregate,不用遍历就可以获取结果;若是annotate,需要遍历,并使用'查询关键字'属性来获取结果
● Max,Min---最大值和最小值
示例:
from django.db.models import Avg,Count,Max,Min
def index2(request):
max_min_age=Author.objects.aggregate(max_age=Max('age'),min_age=Min('age')) # 合并查询
print(max_min_age) # {'min_age': 28, 'max_age': 46}
print(connection.queries) # 'SELECT MIN(`author`.`age`) AS `min_age`, MAX(`author`.`age`) AS `max_age` FROM `author`'
return HttpResponse('index2')
实现需求---获取每一本书在销售时的最大价格值和最小价格值
示例:
def index5(request):
results=Book.objects.annotate(max_price=Max('bookorder__price'),
min_price=Min('bookorder__price'))
for result in results:
print(result.name,':',result.max_price,result.min_price)
'''
西游记 : None None
三国演义 : 220.0 180.0
红楼梦 : 230.0 215.0
水浒传 : None None
'''
print(connection.queries)
return HttpResponse('index5')
● Sum函数---统计字段的所有'值'的和
示例:
统计所有图书的价格总和(就是统计price字段所有值的和)
def index6(request):
result=Book.objects.aggregate(price_total=Sum('price'))
print(result) # {'price_total': 685.0}
print(connection.queries)
return HttpResponse('index6')
获取每一本书的销售总额
def index6(request):
results=Book.objects.annotate(total_price=Sum('bookorder__price'))
for result in results:
print(result.name,':',result.total_price)
'''
西游记 : None
三国演义 : 605.0
红楼梦 : 887.0
水浒传 : None
'''
print(connection.queries)
return HttpResponse('index6')
结果符合预期
aggregate和annotate的区别:
aggregate:返回使用聚合函数后的字段和值,返回类型是dict.
annotate:在原来模型字段的基础之上添加一个使用了聚合函数的字段,并且在使用聚合函数的时候,会使用当前这个模型的主键进行分组(group by)。
比如以上Sum的例子,如果使用的是annotate,那么将在每条图书的数据上都添加一个字段叫做total,计算这本书的销售总额。
而如果使用的是aggregate,那么将求所有图书的销售总额。
• 链式查询(复合查询,多个条件一起查询)
实现需求,获取2019年度bookorder表,price字段所有值的和
获取2019年度图书的销售总额
def index2(request):
# aggregate()方法,是QuerySet对象都可以调用
# result返回的是一个字典,和之前的操作一样
result=BookOrder.objects.filter(create_time__year=2019).aggregate(
total=Sum('price'))
print(result)
print(connection.queries)
return HttpResponse('index2')
获取2019年度每一本图书的销售总额
def index2(request):
books=Book.objects.filter(bookorder__create_time__year=2019).annotate(
every_total=Sum('bookorder__price'))
for book in books:
print(book.name,':',book.every_total)
'''
十万个为什么 : 390.0
生活知识百科 : 190.0
# 其他图书为什么没返回None???
'''
print(connection.queries)
return HttpResponse('index2')
● F/Q表达式---Find表达式,Query表达式
F表达式是用来优化ORM操作数据库的。比如我们要将公司所有员工的薪水都增加1000元,如果按照正常的流程,应该是先从数据库中提取所有的员工工资到Python内存中,然后使用Python代码在员工工资的基础之上增加1000元,最后再保存到数据库中。这里面涉及的流程就是,首先从数据库中提取数据到Python内存中,然后在Python内存中做完运算,之后再保存到数据库中。示例代码如下:
stuff.models
from django.db import models
class Person(models.Model):
name=models.CharField(max_length=50)
salary=models.FloatField()
class Meta:
db_table='person'
stuff.views
from django.shortcuts import render
from .models import Person
from django.http import HttpResponse
def add_salary(request):
persons=Person.objects.all()
for person in persons:
person.salary+=1000
person.save()
return HttpResponse('add salary successful!')
刷新数据库,成功实现期望
而我们的F表达式就可以优化这个流程,他可以不需要先把数据从数据库中提取出来,计算完成后再保存回去,他可以直接执行SQL语句,就将员工的工资增加1000元。示例代码如下:
from django.shortcuts import render
from .models import Person
from django.http import HttpResponse
from django.db.models import F
def add_salary(request):
# persons=Person.objects.all()
# for person in persons:
# person.salary+=1000
# person.save()
# 操作方法:update(keyword=F('attribute')
result=Person.objects.update(salary=F('salary')+1000) # 这里的关键字参数,要与model里的属性相同,不能随便命名
return HttpResponse('add salary successful!')
刷新数据表,结果符合预期
以之前的aggregate demo为示例,把每一本书的售价都加10元,演示如下:
from django.db.models import F
def index3(request):
price=Book.objects.update(price=F('price')+10) # 调用update方法,关键字参数传入F表达式
print(connection.queries[-1]) # 该属性返回字典,我们只需要最后一个就够了...
return HttpResponse('index3')
'''
{'sql': 'UPDATE `book` SET `price` = (`book`.`price` + 10)', 'time': '0.036'}
'''
小结:F表达式的作用,就是动态获取某个字段的值,由于是贴近SQL操作,故效率比普通的代码示例高很多.
比如如果想要获取作者中,name和email相同的作者数据。如果不使用F表达式,那么需要使用以下代码来完成:
authors = Author.objects.all()
for author in authors:
if author.name == author.email:
print(author)
如果使用F表达式,那么一行代码就可以搞定。示例代码如下:
from django.db.models import F
def index3(request):
results=Author.objects.filter(name=F('email'))
for result in results:
print(result.name,':',result.email) # cxq@qq.com : cxq@qq.com wce@qq.com : wce@qq.com
print(connection.queries[-1])
'''
'sql': 'SELECT `author`.`id`, `author`.`name`, `author`.`age`, `author`.`email` FROM `author` WHERE `author`.`name` = (`author`.`email`)'
'''
return HttpResponse('index3')
● Q表达式---更为高效的查询作用
不使用Q表达式,如果想要实现所有价格高于90元,并且评分达到3.9以上评分的图书。那么可以通过以下代码来实现:
def index3(request):
results=Book.objects.filter(price__gte=90,rating__gte=3.9)
for result in results:
print(result.name,':',result.price,result.rating)
print(connection.queries[-1])
return HttpResponse('index3')
'''
生活知识百科 : 122.3 4.0
战争知识百科全书 : 225.6 4.0
{'sql': 'SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id` FROM `book` WHERE (`book`.`price` >= 90.0e0 AND `book`.`rating` >= 3.9e0)', 'time': '0.037'}
'''
以上这个案例是一个并集查询,可以简单的通过传递多个条件进去来实现。
但是如果想要实现一些复杂的查询语句,比如要查询所有价格大于250元,或者是评分低于3.9分的图书。那就没有办法通过传递多个条件进去实现了。这时候就需要使用Q表达式来实现了。示例代码如下:
from django.db.models import Q
def index3(request):
# 查询所有价格大于250元,或者是评分低于3.9分的图书。
results=Book.objects.filter(Q(price__gte=250) | Q(rating__lte=3.9))
for result in results:
print(result.name,':',result.price,result.rating)
print(connection.queries[-1])
return HttpResponse('index3')
'''
十万个为什么 : 72.5 3.0
天气预报常识 : 86.4 3.0
{'sql': 'SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id` FROM `book` WHERE (`book`.`price` >= 250.0e0 OR `book`.`rating` <= 3.9e0)', 'time': '0.001'}
'''
以上是进行或运算,当然还可以进行其他的运算,比如有&和~(非)等,示例:
def index3(request):
# 获取price>=100,并且name不包含'为什么'的结果
results=Book.objects.filter(Q(price__gte=100) & ~Q(name__icontains='为什么'))
for result in results:
print(result.name,':',result.price,result.rating)
print(connection.queries[-1])
return HttpResponse('index3')
一些用Q表达式的例子如下:
from django.db.models import Q
获取id等于3的图书
book=Book.objects.filter(id=3)
books = Book.objects.filter(Q(id=3))
获取id等于3,或者名字中包含文字"记"的图书
books = Book.objects.filter(Q(id=3)|Q(name__contains("记")))
获取价格大于100,并且书名中包含"记"的图书
books = Book.objects.filter(Q(price__gte=100)&Q(name__contains("记")))
获取书名包含“记”,但是id不等于3的图书
books = Book.objects.filter(Q(name__contains='记') & ~Q(id=3))
小结:其实不使用F/Q表达式,而使用普通的python代码也可以实现效果
而使用F/Q表达式确实方便,F/Q表达式的作用,就是优化ORM操作,使代码更为简洁
F表达式和Q表达式的区别:
F表达式的作用是获取字段----值
Q表达式的作用就是查询---一般和filter()搭配使用
QuerySet API
所谓'API',无非就是一些方法
● 研究---objects
我们通常做查询操作的时候,都是通过'ModelName.objects'的方式进行操作。
其实模型名字.objects是一个django.db.models.manager.Manager对象,而Manager这个类是一个“空壳”的类,他本身是没有任何的属性和方法的。
他的方法全部都是通过Python动态添加的方式,从QuerySet类中拷贝过来的
示例:
def index4(request):
books=Book.objects
print(type(books)) # <class 'django.db.models.manager.Manager'>
return HttpResponse('index4')
可以看出,objects实际是属于Manager类,而查看源码,发现Manager是一个空类:
class Manager(BaseManager.from_queryset(QuerySet)): # 这个方法肯定返回一个父类,让Manager类继承
pass # 所以,objects所拥有的方法,肯定来自这个父类
查看from_queryset()方法源码:
@classmethod
def from_queryset(cls, queryset_class, class_name=None): # cls表示当前类
if class_name is None:
class_name = '%sFrom%s' % (cls.name, queryset_class.name)
class_dict = {
'_queryset_class': queryset_class,
}
class_dict.update(cls._get_queryset_methods(queryset_class))
# type第一个参数是指定要创建的'类名'
# 第二个参数是指定要继承的'父类'
# 第三个参数是定义类的一些属性和方法
return type(class_name, (cls,), class_dict)# type的另外一个重要功能,动态创建一个类
下来具体介绍常用的方法
• filter()---表示过滤,接收的参数,可以是关键字参数 filter(id=1) ,也可以是F/Q表达式 filter(~Q(id=2))
它返回的是QuerySet对象,支持链式查询
示例:
筛选id>=2,并且id不为3的所有图书
def index5(request):
books=Book.objects.filter(id__gte=2).filter(~Q(id=3)) # 这里不能写成以下这种形式
for book in books: # books=Book.objects.filter(id__gte=2).filter(id!=3)
print(book.name,book.id)
return HttpResponse('index5')
结合exclude方法,其实还可以这么处理,而且这种写法比较容易读:
def index5(request):
#books=Book.objects.filter(id__gte=2).filter(~Q(id=3))
books=Book.objects.filter(id__gte=2).exclude(id=3) # books存储的是QuerySet对象
for book in books:
print(book.name,book.id)
return HttpResponse('index5')
• 比如过滤完后还要根据某个字段进行排序,那么这一系列的操作我们可以通过一个非常流畅的链式调用的方式进行。比如要从书籍中获取标题为'红楼',并且提取后要将结果根据发布的时间'create_time'进行排序,那么可以使用以下方式来完成:
def index4(request):
books=Book.objects.filter(name__icontains='红楼').order_by('create_time') # '-create_time',排序将会反过来
for book in books:
print(book.name,book.create_time)
'''
红楼梦 2019-11-05 14:44:38+00:00
西游记红楼 2019-12-16 09:43:50+00:00
'''
return HttpResponse('index4')
可以看到'order_by'方法是直接在'filter'执行后调用的。这说明'filter'返回的对象是一个拥有'order_by'方法的对象。而这个对象正是一个新的'QuerySet'对象。因此可以使用'order_by'方法
• exclude---排除满足条件的数据(还可以使用非Q表达式来排除.)
它传参的方式,和filter()是一模一样的...
示例:
返回除了name包含'红楼'的所有图书
def index4(request):
books=Book.objects.exclude(name__icontains='红楼') # 排除name包含'红楼'的字段
for book in books:
print(book.name,book.create_time)
return HttpResponse('index4')
'''
十万个为什么 2019-12-18 01:42:23+00:00
生活知识百科 2019-12-18 01:42:23+00:00
战争知识百科全书 2019-12-18 01:42:23+00:00
天气预报常识 2019-12-18 01:42:23+00:00
'''
练习使用Q表达式,再进行一层过滤
def index9(request):
books=Book.objects.exclude(name__icontains='红楼').filter(~Q(name__icontains='西游'))
for book in books:
print(book)
return HttpResponse('index9')
'''
Book object (2)
Book object (4)
'''
• annotate(英文释义---'注释'):给QuerySet中的每个对象都添加一个使用查询表达式(聚合函数、F表达式、Q表达式、Func表达式等)的新字段
注意,根据解释,是给QuerySet对象增加新字段,什么字段呢---使用了'查询表达式'的'字段'
API---annotate(New_Attribute_Name=Avg('price')) 或者 annotate(New_Attribute_Name=F('author__name')
示例代码如下:
获取每一本书的作者,打印书名和作者名
不使用annotate
books=Book.objects.all()
for book in books:
print(book.name,book.author.name)
print(connection.queries)
return HttpResponse('index5')
'''
十万个为什么 曹雪芹
生活知识百科 吴承恩
战争知识百科全书 罗贯中
天气预报常识 施耐庵
西游记红楼 吴承恩
红楼梦 曹雪芹
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.001'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, `book`.`create_time` FROM `book`', 'time': '0.000'}, {'sql': 'SELECT VERSION()', 'time': '0.000'}, {'sql': 'SELECT `author`.`id`, `author`.`name`, `author`.`age`, `author`.`email` FROM `author` WHERE `author`.`id` = 1', 'time': '0.000'}, {'sql': 'SELECT `author`.`id`, `author`.`name`, `author`.`age`, `author`.`email` FROM `author` WHERE `author`.`id` = 2', 'time': '0.001'}, {'sql': 'SELECT `author`.`id`, `author`.`name`, `author`.`age`, `author`.`email` FROM `author` WHERE `author`.`id` = 3', 'time': '0.000'}, {'sql': 'SELECT `author`.`id`, `author`.`name`, `author`.`age`, `author`.`email` FROM `author` WHERE `author`.`id` = 4', 'time': '0.000'}, {'sql': 'SELECT `author`.`id`, `author`.`name`, `author`.`age`, `author`.`email` FROM `author` WHERE `author`.`id` = 2', 'time': '0.000'}, {'sql': 'SELECT `author`.`id`, `author`.`name`, `author`.`age`, `author`.`email` FROM `author` WHERE `author`.`id` = 1', 'time': '0.000'}]
'''
使用annotate示例
def index5(request):
books=Book.objects.annotate(author_name=F('author__name'))
for book in books:
print(book.name,book.author_name)
print(connection.queries)
'''
十万个为什么 曹雪芹
红楼梦 曹雪芹
生活知识百科 吴承恩
西游记红楼 吴承恩
战争知识百科全书 罗贯中
天气预报常识 施耐庵
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.001'}, {'sql': 'SELECT book
.id
, book
.name
, book
.pages
, book
.price
, book
.rating
, book
.author_id
, book
.publisher_id
, book
.create_time
, author
.name
AS author_name
FROM book
INNER JOIN author
ON (book
.author_id
= author
.id
)', 'time': '0.000'}]
'''
结论:相比示例1,示例2的SQL查询显然少了很多,虽然一样的效果,但是显然示例2的效率,速度都很好
'获取每一本书的作者,打印书名和作者名'---思路就是使用annotate()给QuerySet对象添加新字段,传入F表达式来达到目的,优化查询效率
• order_by:指定将查询的结果根据某个字段进行排序。如果要倒叙排序,那么可以在这个字段的前面加一个负号。示例代码如下:
根据创建的时间正序排序
articles = Article.objects.order_by("create_time")
根据创建的时间倒序排序
articles = Article.objects.order_by("-create_time")
根据作者的名字进行排序
<QuerySet [<Book: 生活知识百科>, <Book: 西游记红楼>, <Book: 天气预报常识>, <Book: 十万个为什么>, <Book: 红楼梦>, <Book: 战争知识百科全书>]>
articles = Article.objects.order_by("author__name") # 属于跨表操作,若是本表操作,直接传入name字段即可
'''若是依据name进行排序,排序的依据是啥...'''
链式查询操作
首先根据创建的时间进行排序,如果时间相同,则根据作者的名字进行排序
articles = Article.objects.order_by("create_time",'author__name')
不使用annotate和使用annotate示例---获取bookorder外键表的rating字段:
def index6(request):
#orders=BookOrder.objects.order_by('book__rating')
orders=BookOrder.objects.annotate(book_rating=F('book__rating')).order_by('-book__rating')
for order in orders:
print(order.id,order.book_rating)
return HttpResponse('index6')
'''
6 4.0
4 4.0
5 4.0
1 3.0
2 3.0
3 3.0
'''
提取图书数据,并对图书的销量进行排序(从大到小)
def index7(request):
books=Book.objects.annotate(max_sale=Count('bookorder')).order_by('max_sale')
for book in books:
print(book.name,book.max_sale)
return HttpResponse('index7')
'''
西游记红楼 0
天气预报常识 0
战争知识百科全书 0
红楼梦 0
十万个为什么 3
生活知识百科 3
'''
关于排序,其实有可以在'Models'里面处理,示例:
class Book(model.Model):
...
class Meta:
ording=['-create_time']
传入字段名和传入F表达式,其实是一样的:
def index7(request):
books=Book.objects.order_by('-create_time')
#books=Book.objects.order_by(F('create_time')) # 查看SQL语句,一样的效果
for book in books:
print(book.name,book.create_time)
print(connection.queries)
return HttpResponse('index7')
注意:多个order_by,会把前面排序的规则给打乱,而使用后面的排序方式。比如以下代码:
会根据作者的名字进行排序,而不是使用文章的创建时间。
articles = Article.objects.order_by("create_time").order_by("author__name")
• values:用来指定在提取数据出来,需要提取哪些字段。不传参会把表中所有的字段全部都提取出来,并且使用了values后,提取出的QuerySet中的数据类型不是'模型',而是在values方法中指定的字段和值形成的'字典':
def index4(request):
books=Book.objects.values('name','price') # 提取name,price字段的值,返回键值对字典
for book in books: # 注意,这里的books依然是QuerySet对象,这个QuerySet对象存储的字典
print(book)
return HttpResponse('index4')
'''
{'name': '十万个为什么', 'price': 72.5}
{'name': '生活知识百科', 'price': 122.3}
{'name': '战争知识百科全书', 'price': 225.6}
{'name': '天气预报常识', 'price': 86.4}
{'name': '西游记红楼', 'price': 100.0}
{'name': '红楼梦', 'price': 230.0}
'''
若想实现跨表查询,参数该怎么传?
示例:
def index7(request):
results=Book.objects.values('id','name','author__name') # 依旧是'模型名小写+双下划线字段名形式'
# 这种写法,一般用于位置参数(不能用于关键字参数)
print(type(results))
for result in results:
print(result)
return HttpResponse('index7')
'''
{'author__name': '曹雪芹', 'name': '红楼梦', 'id': 3}
{'author__name': '吴承恩', 'name': '西游记', 'id': 1}
{'author__name': '罗贯中', 'name': '三国演义', 'id': 2}
{'author__name': '施耐庵', 'name': '水浒传', 'id': 4}
'''
这里有个问题,'author__name'这个键名不是很友好,想自定义键的名称,该怎么处理?以下示例是否可行:
def index7(request):
results=Book.objects.values('id','name',author_name='author__name') # 使用关键字参数
print(type(results))
for result in results:
print(result)
return HttpResponse('index7')
结果报错:'''TypeError at /QuerySet.annotate() received non-expression(s): author__name.'''
正确的处理方式---使用关键词参数,值为F表达式
def index7(request):
results=Book.objects.values('id','name',author_name=F('author__name')) # 传入F表达式来
print(type(results))
for result in results:
print(result)
return HttpResponse('index7')
'''
{'author_name': '曹雪芹', 'id': 3, 'name': '红楼梦'} # 键名已变更
{'author_name': '吴承恩', 'id': 1, 'name': '西游记'}
{'author_name': '罗贯中', 'id': 2, 'name': '三国演义'}
{'author_name': '施耐庵', 'id': 4, 'name': '水浒传'}
'''
注意:这里为什么不能使用Q表达式?因为Q表达式传入的参数是关键字参数,表示查询
而F表达式定位的是字段的'值',而'字段值'正是我们所需要的
这里不能这么写:
def index8(request):
books=Book.objects.values('id','name',F('author__name')) # 不能直接传入F表达式,会报错
for book in books:
print(book.id,book.name,book.author_name) # 也不能这么写,因为book已经是字典了,不再是QuerySet对象
return HttpResponse('index8')
使用关键字参数,不仅可以传入F表达式,还可以传入聚合函数
示例:
def index7(request):
results=Book.objects.values('id','name',order_numbers=Count('bookorder__id')) # 跨表统计id
print(type(results))
for result in results:
print(result)
return HttpResponse('index7')
'''
{'id': 1, 'order_numbers': 0, 'name': '西游记'}
{'id': 2, 'order_numbers': 3, 'name': '三国演义'}
{'id': 3, 'order_numbers': 4, 'name': '红楼梦'}
{'id': 4, 'order_numbers': 0, 'name': '水浒传'}
'''
注意:若在values中没有传递任何参数,那么将会返回模型中所有的字段值,形成的键值对字典,这里不再示例.
• values_list():类似于values()。只不过返回的QuerySet中,存储的不是字典,而是元组。
示例:
def index4(request):
books=Book.objects.values_list('name','price') # 返回的是元组...
for book in books: # 既然返回的是元组,为什么不叫'values_tuple()???'
print(book)
return HttpResponse('index4')
'''
('十万个为什么', 72.5)
('生活知识百科', 122.3)
('战争知识百科全书', 225.6)
('天气预报常识', 86.4)
('西游记红楼', 100.0)
('红楼梦', 230.0)
'''
注意:如果在values_list中只有一个字段(若两个或以上,就没有效果)。那么你可以传递flat=True来将结果扁平化(更加友好)。示例代码如下:
def index4(request):
books=Book.objects.values_list('price',flat=True)
print(books) # <QuerySet [72.5, 122.3, 225.6, 86.4, 100.0, 230.0]>
# 删除flat参数效果:<QuerySet [(72.5,), (122.3,), (225.6,), (86.4,), (100.0,), (230.0,)]>
return HttpResponse('index4')
all:获取这个ORM模型所有的QuerySet对象。遍历过后,显示模型'str'返回的文本
示例:
views
def index4(request):
books=Book.objects.all()
print(books)
for book in books:
print(book)
return HttpResponse('index4')
'''
<QuerySet [<Book: 十万个为什么>, <Book: 生活知识百科>, <Book: 战争知识百科全书>, <Book: 天气预报常识>, <Book: 西游记红楼>, <Book: 红楼梦>]>
十万个为什么
生活知识百科
战争知识百科全书
天气预报常识
西游记红楼
红楼梦
'''
models
class Book(models.Model):
......
def __str__(self):
return self.name # 只返回name字段
• select_related:在提取某个模型的数据的同时,也'提前'将相关联的'模型'提取出来(这意味着可以通过'提前的关联模型'获取'关联字段')。比如提取文章数据,可以使用select_related将author模型提取出来,以后再次使用article.author的时候就不需要再次去访问数据库了。
作用---可以减少数据库查询的次数,提高效率
示例代码如下:
article = Article.objects.get(pk=1) # 执行一次查询语句
article.author # 再次执行一次查询语句
这里实际可以看成熟悉的模式代码:article = Article.objects.get(pk=2) # 只不过添加了一个关联模型而已
注意,以下的语句获取的id=2的 article 数据,跟 author 模型无关,不要认为是获取到id=2的 author
article = Article.objects.select_related("author").get(pk=2) # 执行一次查询语句
article.author # 不需要重新执行查询语句了
select_related
自己的试验示例:
def index8(request):
results=Book.objects.select_related('author','publisher')
print(type(results)) # <class 'django.db.models.query.QuerySet'> 依旧是QuerySet对象
for result in results:
print(result.author.name,result.publisher.name)
return HttpResponse('index8')
'''
吴承恩 福建人民出版社
罗贯中 人民大学出版社
曹雪芹 中国邮电出版社
施耐庵 清华大学出版社
'''
注意:'只能'用在'一对多'或者'一对一'中,'不能'用在'多对多'或者'多对一'中。比如可以提前获取文章的作者,但是不能通过作者获取这个作者的文章,或者是通过某篇文章获取这个文章所有的标签。
上面其实可以小结成一句话,范围适用---models.ForeignKey()的形式
• prefetch_related---
先看示例,获取书名后,在获取每一本书的销量id:
def index9(request):
books=Book.objects.all()
for book in books:
print(book.name)
orders=book.bookorder_set.all()
for order in orders:
print(order.id)
print(connection.queries)
return HttpResponse('index9')
'''
十万个为什么
1
2
3
生活知识百科
4
5
6
战争知识百科全书
天气预报常识
西游记红楼
红楼梦
......一大堆的SQL语句
'''
上述示例,虽然满足需求,但是缺点很明显,SQL的性能开销不小...现在使用prefetch_related
基础示例:
def index9(request):
books=Book.objects.select_related('author') # 单纯的Book.objects是无法获取字段的
for book in books:
print(book.name)
print(connection.queries)
return HttpResponse('index9')
'''
十万个为什么
红楼梦
生活知识百科
西游记红楼
战争知识百科全书
天气预报常识
[{'time': '0.001', 'sql': 'SELECT @@SQL_AUTO_IS_NULL'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.001', 'sql': 'SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, `book`.`create_time`, `author`.`id`, `author`.`name`, `author`.`age`, `author`.`email` FROM `book` INNER JOIN `author` ON (`book`.`author_id` = `author`.`id`)'}]
'''
更改为:books=Book.objects.prefetch_related('author')
'''
十万个为什么
生活知识百科
战争知识百科全书
天气预报常识
西游记红楼
红楼梦
[{'time': '0.000', 'sql': 'SELECT @@SQL_AUTO_IS_NULL'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.000', 'sql': 'SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, `book`.`create_time` FROM `book`'}, {'time': '0.000', 'sql': 'SELECT VERSION()'}, {'time': '0.001', 'sql': 'SELECT `author`.`id`, `author`.`name`, `author`.`age`, `author`.`email` FROM `author` WHERE `author`.`id` IN (1, 2, 3, 4)'}]
'''
效果虽然一样,但是底层的SQL语句还是有差别的
现在满足需求,获取每一本书的书名,并且在bookorder表的价格大于70的价格
示例:
def index9(request):
# 通过外键表获取主表的字段格式('bookorder_set'),这个名称可以model.py里面修改
# 这里若是主表获取外键表字段,格式就是('bookorder')
books=Book.objects.prefetch_related('bookorder_set')
for book in books:
print(book.name) # 获取书名,运行到这里,SQL语句还是比较少的
orders=book.bookorder_set.filter(price__gte=70) # 获取价格
for order in orders: # 从遍历开始,SQL语句又开始变多了
print(order.id)
print(connection.queries)
return HttpResponse('index9')
'''
十万个为什么
110.0
220.0
生活知识百科
75.0
战争知识百科全书
天气预报常识
西游记红楼
红楼梦
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, `book`.`create_time` FROM `book`', 'time': '0.000'}, {'sql': 'SELECT VERSION()', 'time': '0.000'}, {'sql': 'SELECT `book_order`.`id`, `book_order`.`book_id`, `book_order`.`price`, `book_order`.`create_time` FROM `book_order` WHERE `book_order`.`book_id` IN (1, 2, 3, 4, 5, 6)', 'time': '0.001'}, {'sql': 'SELECT `book_order`.`id`, `book_order`.`book_id`, `book_order`.`price`, `book_order`.`create_time` FROM `book_order` WHERE (`book_order`.`book_id` = 1 AND `book_order`.`price` >= 70.0e0)', 'time': '0.001'}, {'sql': 'SELECT `book_order`.`id`, `book_order`.`book_id`, `book_order`.`price`, `book_order`.`create_time` FROM `book_order` WHERE (`book_order`.`book_id` = 2 AND `book_order`.`price` >= 70.0e0)', 'time': '0.000'}, {'sql': 'SELECT `book_order`.`id`, `book_order`.`book_id`, `book_order`.`price`, `book_order`.`create_time` FROM `book_order` WHERE (`book_order`.`book_id` = 3 AND `book_order`.`price` >= 70.0e0)', 'time': '0.000'}, {'sql': 'SELECT `book_order`.`id`, `book_order`.`book_id`, `book_order`.`price`, `book_order`.`create_time` FROM `book_order` WHERE (`book_order`.`book_id` = 4 AND `book_order`.`price` >= 70.0e0)', 'time': '0.000'}, {'sql': 'SELECT `book_order`.`id`, `book_order`.`book_id`, `book_order`.`price`, `book_order`.`create_time` FROM `book_order` WHERE (`book_order`.`book_id` = 5 AND `book_order`.`price` >= 70.0e0)', 'time': '0.000'}, {'sql': 'SELECT `book_order`.`id`, `book_order`.`book_id`, `book_order`.`price`, `book_order`.`create_time` FROM `book_order` WHERE (`book_order`.`book_id` = 6 AND `book_order`.`price` >= 70.0e0)', 'time': '0.000'}]
'''
上述示例虽然达到需求,缺点也明显,后面的SQL语句查询过多,怎么避免---Prefecth类
示例:
from django.db.models import Avg,Count,Sum,F,Q,Prefetch
def index9(request):
prefetch=Prefetch('bookorder_set', # 预先获取模型并执行模型的查询语句
queryset=BookOrder.objects.filter(price__gte=70))
books=Book.objects.prefetch_related(prefetch)# 预模型传给prefetch_related()
for book in books:
print(book.name)
orders=book.bookorder_set.all()# 这句就不要再执行QuerySet对象的方法了,否则又是白搭
for order in orders: # 要预先把所有的查询语句都放在prefecth()
print(order.id)
print(connection.queries)
return HttpResponse('index9')
'''
十万个为什么
2
3
生活知识百科
5
战争知识百科全书
天气预报常识
西游记红楼
红楼梦
[{'time': '0.001', 'sql': 'SELECT @@SQL_AUTO_IS_NULL'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.001', 'sql': 'SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, `book`.`create_time` FROM `book`'}, {'time': '0.001', 'sql': 'SELECT VERSION()'}, {'time': '0.001', 'sql': 'SELECT `book_order`.`id`, `book_order`.`book_id`, `book_order`.`price`, `book_order`.`create_time` FROM `book_order` WHERE (`book_order`.`price` >= 70.0e0 AND `book_order`.`book_id` IN (1, 2, 3, 4, 5, 6))'}]
'''
• defer---过滤'字段':在一些表中,可能存在很多的字段,但是一些字段的数据量可能是比较庞大的,而此时你又不需要
比如我们在获取文章列表的时候,文章的内容我们是不需要的,因此这时候我们就可以使用defer来过滤掉一些字段。
这个字段跟values有点类似,只不过defer返回的不是字典,而是模型('''这句话怎么理解?''')
示例1:
def index4(request):
books=Book.objects.defer('create_time','pages','price') # 过滤掉这三个字段
print(books) # <QuerySet [<Book: 十万个为什么>, <Book: 生活知识百科>, <Book: 战争知识百科全书>, <Book: 天气预报常识>, <Book: 西游记红楼>, <Book: 红楼梦>]> # 可以看到,返回的也是QuerySet对象,为啥上面说是返回模型???
for book in books:
print(book)
'''
十万个为什么
生活知识百科
战争知识百科全书 # 显然,没看出效果
天气预报常识
西游记红楼
红楼梦
'''
# 这里查看SQL语句,就有效果了,显然,defer()包含的字段被过滤掉了
print(connection.queries)
'''
'sql': 'SELECT book
.id
, book
.name
, book
.rating
, book
.author_id
, book
.publisher_id
FROM book
'}]
'''
示例2:
def index4(request):
books=list(Book.objects.defer('create_time','pages','price')) # 使用list()处理后,返回不再是QuerySet类型,而是list类型了
print(type(books))
print(books)
for sql in connection.queries:
print('*'*40)
print(sql)
return HttpResponse('index4')
'''
<class 'list'>
[<Book: 十万个为什么>, <Book: 生活知识百科>, <Book: 战争知识百科全书>, <Book: 天气预报常识>, <Book: 西游记红楼>, <Book: 红楼梦>]
****************************************
{'time': '0.000', 'sql': 'SELECT @@SQL_AUTO_IS_NULL'}
****************************************
{'time': '0.001', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}
****************************************
{'time': '0.000', 'sql': 'SELECT `book`.`id`, `book`.`name`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id` FROM `book`'}
'''
注意:此时虽然有些字段被过滤了,但是若想重新显示,可以这么处理:
def index4(request):
books=list(Book.objects.defer('create_time','pages','price'))
for book in books:
print(book.create_time) # 上面过滤掉了'create_time',这里会重新进行数据库查询
for sql in connection.queries:
print('*'*40)
print(sql)
return HttpResponse('index4')
'''
2019-12-18 01:42:23+00:00
2019-12-18 01:42:23+00:00
2019-12-18 01:42:23+00:00
2019-12-18 01:42:23+00:00
2019-12-16 09:43:50+00:00
2019-11-05 14:44:38+00:00
****************************************
{'time': '0.001', 'sql': 'SELECT @@SQL_AUTO_IS_NULL'}
****************************************
{'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}
****************************************
# 三个defer字段依然没有
{'time': '0.001', 'sql': 'SELECT `book`.`id`, `book`.`name`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id` FROM `book`'}
****************************************
{'time': '0.000', 'sql': 'SELECT VERSION()'}
****************************************
# 从这里开始,又进行了'create_time'查询
{'time': '0.000', 'sql': 'SELECT `book`.`id`, `book`.`create_time` FROM `book` WHERE `book`.`id` = 1'}
****************************************
{'time': '0.001', 'sql': 'SELECT `book`.`id`, `book`.`create_time` FROM `book` WHERE `book`.`id` = 2'}
****************************************
{'time': '0.001', 'sql': 'SELECT `book`.`id`, `book`.`create_time` FROM `book` WHERE `book`.`id` = 3'}
****************************************
{'time': '0.000', 'sql': 'SELECT `book`.`id`, `book`.`create_time` FROM `book` WHERE `book`.`id` = 4'}
****************************************
{'time': '0.000', 'sql': 'SELECT `book`.`id`, `book`.`create_time` FROM `book` WHERE `book`.`id` = 5'}
****************************************
{'time': '0.000', 'sql': 'SELECT `book`.`id`, `book`.`create_time` FROM `book` WHERE `book`.`id` = 6'}
'''
注意: defer虽然能过滤字段,但是有些字段是不能过滤的,比如'id'(主键),即使你过滤了,也会提取出来。
'''和values()的区别'''
values()返回的字典,所有遍历完成后,不能使用比如'book.name'的形式访问,而我们的模型可以这么访问,这就是区别
• only---跟defer类似,只不过defer是过滤掉指定的字段,而only是只提取指定的字段
示例:
def index4(request):
books=Book.objects.only('name') # 只提取name字段
for book in books:
print(book.price) # 被过滤了,依然可以查询获取
for sql in connection.queries:
print('*'*40)
print(sql)
return HttpResponse('index4')
'''
72.5
122.3
225.6
86.4
100.0
230.0
****************************************
{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.001'}
****************************************
{'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}
****************************************
# 只有name,id字段被提取
{'sql': 'SELECT `book`.`id`, `book`.`name` FROM `book`', 'time': '0.000'}
****************************************
{'sql': 'SELECT VERSION()', 'time': '0.000'}
****************************************
{'sql': 'SELECT `book`.`id`, `book`.`price` FROM `book` WHERE `book`.`id` = 1', 'time': '0.000'}
****************************************
{'sql': 'SELECT `book`.`id`, `book`.`price` FROM `book` WHERE `book`.`id` = 2', 'time': '0.000'}
****************************************
{'sql': 'SELECT `book`.`id`, `book`.`price` FROM `book` WHERE `book`.`id` = 3', 'time': '0.001'}
****************************************
{'sql': 'SELECT `book`.`id`, `book`.`price` FROM `book` WHERE `book`.`id` = 4', 'time': '0.000'}
****************************************
{'sql': 'SELECT `book`.`id`, `book`.`price` FROM `book` WHERE `book`.`id` = 5', 'time': '0.000'}
****************************************
{'sql': 'SELECT `book`.`id`, `book`.`price` FROM `book` WHERE `book`.`id` = 6', 'time': '0.000'}
[18/Dec/2019 13:41:35] "GET / HTTP/1.1" 200 6
'''
● get()---永远只能获取一条数据,获取多条或者数据不存在,就报错(属于'娇贵'类型)
示例:
获取id>=2的所有数据
def index11(request):
book=Book.objects.get(pk__gte=2)
print(book)
return HttpResponse('index11')
'''
get() returned more than one Book -- it returned 5!
'''
● create()---创建数据并保存
没有这个方法之前,我们是这么创建并保存数据的
def index11(request):
publisher=Publisher(name='厦门出版社')
publisher.save() # 刷新db,成功插入数据
return HttpResponse('index11')
使用create()
def index11(request):
publisher=Publisher.objects.create(name='漳州出版社')
print(connection.queries)
return HttpResponse('index11')
'''
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.001'}, {'sql': "INSERT INTO `publisher` (`name`) VALUES ('漳州出版社')", 'time': '0.000'}]
'''
小结: create()实际可以看成是'类实例化对象,save()的简写版'
● get_or_create---先获取数据,若有,就返回该数据;若没有,就创建这个数据
这个方法返回的类型是'tuple'---包含了模型对象和布尔值的'tuple'
示例:
def index11(request):
publisher=Publisher.objects.get_or_create(name='漳州出版社')
print(type(publisher))
print(publisher)
print(connection.queries)
return HttpResponse('index11')
'''
<class 'tuple'>
(<Publisher: 漳州出版社>, False)
[{'time': '0.000', 'sql': 'SELECT @@SQL_AUTO_IS_NULL'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.000', 'sql': 'SELECT VERSION()'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.000', 'sql': "SELECT `publisher`.`id`, `publisher`.`name` FROM `publisher` WHERE `publisher`.`name` = '漳州出版社'"}]
'''
现在更改一下上述示例,插入一条数据,看看此时SQL语句变化:
def index11(request):
publisher=Publisher.objects.get_or_create(name='泉州出版社')
print(publisher)
print(connection.queries)
return HttpResponse('index11')
'''
(<Publisher: 泉州出版社>, True)
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT VERSION()', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.001'}, {'sql': "SELECT publisher
.id
, publisher
.name
FROM publisher
WHERE publisher
.name
= '泉州出版社'", 'time': '0.000'}, {'sql': "INSERT INTO publisher
(name
) VALUES ('泉州出版社')", 'time': '0.000'}]
'''
# 可以看出先执行了查询语句,然后再执行了插入数据的操作,比之前多了一步...
实际需求,如果数据被删除,那么就使用'默认出版社'代替:
models
class Publisher(models.Model): # Publisher模型
name=models.CharField(max_length=300)
def __str__(self):
return self.name
class Meta:
db_table='publisher'
def get_default_publisher(): # 设置默认值
return Publisher.objects.get_or_create(name='默认出版社')
class Book(models.Model):
name=models.CharField(max_length=300)
pages=models.IntegerField()
price=models.FloatField()
rating=models.FloatField()
author=models.ForeignKey('Author',on_delete=models.CASCADE)
publisher=models.ForeignKey('Publisher',on_delete=models.SET_DEFAULT, # 外键关联
default=get_default_publisher)# 传入函数对象
create_time=models.DateTimeField(auto_now_add=True,null=True)
def __str__(self):
return self.name
class Meta:
db_table='book'
● bulk_create()---批量插入数据
示例:
def index11(request):
publishers=Publisher.objects.bulk_create([ # 接收的参数类型为list
Publisher(name='江苏出版社'), # 这里的批量数据一定要用[]包裹起来,否则会报错
Publisher(name='南京出版社'),
])
print(type(publishers)) # <class 'list'> 返回list类型
print(publishers) # [<Publisher: 江苏出版社>, <Publisher: 南京出版社>] list里面是两个Publisher模型对象
for publisher in publishers: # 既然是模型对象,当然可以遍历...
print(publisher.name)
print(connection.queries)
return HttpResponse('index11')
● count()---统计个数
不使用count(),使用len(),执行的效率不高
def index14(request):
book=Book.objects.all() # all()返回QuerySet对象
count_numbers=len(book) # 6
print(count_numbers)
print(connection.queries)
return HttpResponse('index14')
'''
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT book
.id
, book
.name
, book
.pages
, book
.price
, book
.rating
, book
.author_id
, book
.publisher_id
, book
.create_time
FROM book
', 'time': '0.001'}]
'''
使用count()
def index14(request):
count_numbers=Book.objects.count()
print(count_numbers) # 6
print(connection.queries)
return HttpResponse('index14')
'''
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT COUNT(*) AS `__count` FROM `book`', 'time': '0.000'}]
'''
可以看到,使用count()的效率,明显高了很多.
● exists()---判断某个元素是否存在,返回布尔值.它的SQL效率最高(虽然也有其他方法)
示例:
def index14(request):
result=Book.objects.filter(name__icontains='为什么').exists()
print(result)
print(connection.queries)
return HttpResponse('index14')
'''
True
[{'time': '0.000', 'sql': 'SELECT @@SQL_AUTO_IS_NULL'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.001', 'sql': 'SELECT VERSION()'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.020', 'sql': "SELECT (1) AS a
FROM book
WHERE book
.name
LIKE '%为什么%' LIMIT 1"}]
'''
不适用exits(),使用count()示例:
def index14(request):
#result=Book.objects.filter(name__icontains='为什么').exists()
result=Book.objects.filter(name__icontains='为什么').count()>0
print(result) # 返回True
print(connection.queries)
return HttpResponse('index14')
'''
[{'time': '0.000', 'sql': 'SELECT @@SQL_AUTO_IS_NULL'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.000', 'sql': 'SELECT VERSION()'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.001', 'sql': "SELECT COUNT(*) AS `__count` FROM `book` WHERE `book`.`name` LIKE '%为什么%'"}]
'''
这么处理,效率其实差不多...但是exists()更易读
还可以这么写:
def index14(request):
#result=Book.objects.filter(name__icontains='为什么').exists()
#result=Book.objects.filter(name__icontains='为什么').count()>0
if Book.objects.filter(name__icontains='为什么'):
print(True)
print(connection.queries)
return HttpResponse('index14')
'''
[{'time': '0.001', 'sql': 'SELECT @@SQL_AUTO_IS_NULL'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.000', 'sql': 'SELECT VERSION()'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.001', 'sql': "SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, `book`.`create_time` FROM `book` WHERE `book`.`name` LIKE '%为什么%'"}]
'''
这个效率最低...
● distinct()---去除重复项
获取预定表,价格大于90的图书价格
def index14(request):
books=Book.objects.filter(bookorder__price__gte=90)
for book in books:
print(book)
print(connection.queries)
return HttpResponse('index14')
'''
十万个为什么
十万个为什么
生活知识百科
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.001'}, {'sql': 'SELECT VERSION()', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, `book`.`create_time` FROM `book` INNER JOIN `book_order` ON (`book`.`id` = `book_order`.`book_id`) WHERE `book_order`.`price` >= 90.0e0', 'time': '0.024'}]
'''
可以看出,不需要重复的书名,此时就可以用distinct()处理后
示例:
def index14(request):
books=Book.objects.filter(bookorder__price__gte=90).distinct() # 在原来的基础上,加了distinct()
for book in books:
print(book)
print(connection.queries)
return HttpResponse('index14')
'''
十万个为什么 # 删除了重复项
生活知识百科
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT VERSION()', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT DISTINCT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, `book`.`create_time` FROM `book` INNER JOIN `book_order` ON (`book`.`id` = `book_order`.`book_id`) WHERE `book_order`.`price` >= 90.0e0', 'time': '0.059'}]
'''
注意事项,若使用distinct()之前,使用了order_by或者annotate(),那么,是没有效果的,也就是说,重复项不会被抛弃
示例:
def index14(request):
# annotate示例
# 先定义个新属性,获取预定表的销售图书价格,然后筛选大于90的价格,最后执行删除重复项操作
books=Book.objects.annotate(order_price=F('bookorder__price')).filter(bookorder__price__gte=90).distinct()
for book in books:
print(book)
print(connection.queries)
return HttpResponse('index14')
'''
十万个为什么
十万个为什么
十万个为什么 # 没有效果
生活知识百科
生活知识百科
生活知识百科
[{'time': '0.001', 'sql': 'SELECT @@SQL_AUTO_IS_NULL'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.001', 'sql': 'SELECT VERSION()'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.019', 'sql': 'SELECT DISTINCT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, `book`.`create_time`, `book_order`.`price` AS `order_price` FROM `book` LEFT OUTER JOIN `book_order` ON (`book`.`id` = `book_order`.`book_id`) INNER JOIN `book_order` T3 ON (`book`.`id` = T3.`book_id`) WHERE T3.`price` >= 90.0e0'}]
'''
order_by()示例
def index14(request):
# 获取预定表图书大于90的书籍,并且从小到大排序
books=Book.objects.filter(bookorder__price__gte=90).order_by('bookorder__price').distinct()
for book in books:
print(book)
print(connection.queries)
return HttpResponse('index14')
'''
十万个为什么
生活知识百科 # 重复项不会删除
十万个为什么
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT VERSION()', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT DISTINCT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_id`, `book`.`publisher_id`, `book`.`create_time`, `book_order`.`price` FROM `book` INNER JOIN `book_order` ON (`book`.`id` = `book_order`.`book_id`) WHERE `book_order`.`price` >= 90.0e0 ORDER BY `book_order`.`price` ASC', 'time': '0.001'}]
'''
● update()---批量更新字段值
不使用update
def index15(request):
books=Book.objects.all() # 获取QuerySet对象
for book in books:
book.price+=10 # 遍历,属性赋值
book.save()
return HttpResponse('index15')
使用update()
def index15(request):
add_price=Book.object.update(price=F('price')+10) # 传入字段名关键字参数的F表达式
return HttpResponse('index15')
● delete()---删除数据,删的时候,注意'on_delete'的删除方式,不再示例
QuerySet对象的切片操作
● get_queryset()---获取QuerySet对象,其实这个方法和all()是一样的,all()不是字面上的意思,返回所有
而是获取QuerySet对象,示例:
def index15(request):
# 获取id=1,2,3,4总共4本书
books=Book.objects.get_queryset()[0:4] # 这里若换成all()方法,一样的效果
for book in books:
print(book)
return HttpResponse('index15')
'''
十万个为什么
生活知识百科
战争知识百科全书
天气预报常识
'''
● Django执行SQL语句的时机---把握好时机,可以优化SQL查询,提高效率,节省开销
<1> 迭代
<2> 切片之---"步长"操作了
<3> 使用len()
<4> 使用list()
<5> 判断,即对QuerySet对象使用判断
请注意这两个语句的区别:
def index17(request):
books = Book.objects.all()
print(connection.queries) # [] 查看django执行的SQL语句
print(books.query) # SELECT book
.id
, book
.name
...... # 把books orm代码转为SQL语句
return HttpResponse('index17')
ORM Review:https://www.django.cn/course/show-18.html
● 模型迁移
• makemigrations:将模型生成迁移脚本。模型所在的app,必须放在settings.py中的INSTALLED_APPS中。这个命令有以下几个常用选项:
<1> app_label:后面可以跟一个或者多个app,那么就只会针对这几个app生成迁移脚本。如果没有任何的app_label,那么会检查INSTALLED_APPS中所有的app下的模型,针对每一个app都生成响应的迁移脚本。
<2> --name:给这个迁移脚本指定一个名字。
示例
python manage.py makemigrations front --name rename_front # rename_front表示重新命名的文件名称
<3> --empty:生成一个空的迁移脚本。如果你想写自己的迁移脚本,可以使用这个命令来实现一个空的文件,然后自己再在文件中写迁移脚本。
示例
python manage.py makemigrations front --empty # 生成空的迁移脚本,可以手动去填写需求...
• migrate---将生成的迁移脚本,映射到(写入)数据库中
<1>使用'migrate'后,数据库中会多一张表---'django_migrations',里面记录着'迁移的脚本文件'
以后每'migrate'一次,新的迁移脚本文件名就会被写入这张数据表.
<2>app_label:将某个app下的迁移脚本映射到数据库中。如果没有指定,那么会将所有在INSTALLED_APPS中的app下的模型都映射到数据库中。app_label migrationname:将某个app下指定名字的migration文件映射到数据库中
示例:
migrate front 0002_author_age
'''小白和大神的区别'''
--fake:可以将指定的迁移脚本名字添加到数据库中。但是并不会把迁移脚本转换为SQL语句,修改数据库中的表。
--fake-initial:将第一次生成的迁移文件版本号记录在数据库中。但并不会真正的执行迁移脚本。
<3> showmigrations:查看某个app下的迁移文件。如果后面没有app,那么将查看INSTALLED_APPS中所有的迁移文件
示例:
sh/showmigrations/showmigrations front # 分别查看效果,不仅有front,cms,还有其他app,例如auth,admin app
<4> sqlmigrate:查看某个迁移脚本在映射到数据库中的时候,转换的SQL语句
示例:
sqlmigrate front 0003_author_gender
'''
Tracking file by folder pattern: migrations
BEGIN;
--
-- Add field gender to author
--
ALTER TABLE front_author
ADD COLUMN gender
varchar(10) NULL;
COMMIT;
'''
以下是关于'migrate'的原版笔记:
migrate怎么判断哪些迁移脚本需要执行:
他会将代码中的迁移脚本和数据库中django_migrations
中的迁移脚本进行对比,如果发现数据库中,没有这个迁移脚本,那么就会执行这个迁移脚本。
migrate做了什么事情:
- 将相关的迁移脚本翻译成SQL语句,在数据库中执行这个SQL语句(比如数据的增删等等,从而改变'表'的结构)
- 如果这个SQL语句执行没有问题,那么就会将这个迁移脚本的名字记录到
django_migrations
中。
执行migrate命令的时候报错的解决办法:
原因:
执行migrate命令会报错的原因是。数据库的django_migrations
表中的迁移版本记录和代码中的迁移脚本不一致导致的。
比如说,现在删除'django_migrations表'中的一条迁移记录
0003_author_gender.py
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('front', '0002_author_age'),
]
operations = [
migrations.AddField(
model_name='author',
name='gender', # 这个记录的作用,就是增加一个'gender'字段
field=models.CharField(max_length=10, null=True),
),
]
现在执行命令:migrate front ,就报错了,提示'gender'字段重复
因为此时的数据表,'gender'字段其实是存在的,但是由于'django_migrations表'中这条记录被删除了
所以django会执行这条记录,结果就报错,解决的办法:
把这条记录加进'django_migrations表',这样djaogo就不会执行这条记录了,就不会报错
操作方法:
migrate front 0003_author_gender --fake
解决办法:
使用--fake参数:ss使用--fake
,将代码中的迁移脚本添加到django_migrations
中,但是并不会执行sql语句。这样就可以避免每次执行migrate
的时候,都执行一些重复的迁移脚本。
终极解决方案:
如果代码中的迁移脚本和数据库中的迁移脚本实在太多,就是搞不清了。那么这时候就可以使用以下终极解决方案:
终极解决方案原理:就是将之前的那些迁移脚本都不用了。重新来过。要将出问题的app下的所有模型和数据库中表保持一致,重新映射:
<1>. 将出问题的app下的所有模型,都和数据库中的表保持一致。
<2>. 将出问题的app下的所有迁移脚本文件都删掉。再在`django_migrations`表中将出问题的app相关的迁移记录都删掉。
<3>. 使用`makemigrations`,重新将模型生成一个迁移脚本。
<4>. 使用`migrate --fake-initial`参数,将刚刚生成的迁移脚本,标记为已经完成(因为这些模型相对应的表,其实都已经在数据库中存在了,不需要重复执行了。)