阅读本文你可以学习到什么
1、查看Django queryset执行的SQL(1部分)
2、获取的查询结果直接以类似list方式展示(2,3部分)
3、如何在django中给一个字段取一个别名(4部分)
4、annotate聚合计数,求和,求平均数等(5部分)
5、优化SQL,减少多对一,一对多,多对多时查询次数(6,7部分)
6、如何只去除需要的字段,排除某些字段(8,9部分)
7、自定义一个自定义聚合功能,比如group_concat(10部分)
新建一个项目zqxt_blog,建一个app名称是blog
django-admin startproject zqxt
python manage.py startapp blog
把blog加入到setting.py中的INSTALLED_APPS中
blog/models.py代码如下
''' |--一篇文章只有一个作者,一个作者可有有多篇文章,一篇文章会有多个标签 ''' from __future__ import unicode_literals from django.db import models from django.utils.encoding import python_2_unicode_compatible # Create your models here. #定义作者类 class Author(models.Model): name = models.CharField(max_length=50) qq = models.CharField(max_length=10) addr = models.TextField() email = models.EmailField() def __str__(self): return self.name #定义文章类 class Article(models.Model): title = models.CharField(max_length=50) author = models.ForeignKey(Author,on_delete=models.CASCADE,) context = models.TextField() score = models.IntegerField() #打分 tags = models.ManyToManyField('Tag') def __str__(self): return self.title #定义标签类 class Tag(models.Model): name = models.CharField(max_length=50) def __str__(self): return self.name
比较简单,假设一篇文章只有一个作者(Author),一个作者可以有多篇文章(Article),一篇文章可以有多个标签(Tag)。
创建migrations然后migrate在数据库中生成相应的表
python manage.py makemigrations
python manage.py migrate
生成一些实例数据,运行initdb.py()
# -*- coding: utf-8 -*- from __future__ import unicode_literals # 将模块中显式出现的所有字符串转为unicode类型 import random # (Web Server Gateway Interface,Web 服务器网关接口)则是Python语言中所定义的Web服务器和Web应用程序之间或框架之间的通用接口标准。 from zqxt_blog.wsgi import * # from blog.models import Author, Article, Tag author_name_list = ['wufuqiang', 'test005', 'jinjin', 'xue', 'wei'] article_title_list = ['Django教程', 'Python教程', 'HTML教程'] def create_authors(): for author_name in author_name_list: #首先尝试获取,不存在就创建,可以防止重复 author, create = Author.objects.get_or_create(name=author_name) # 随机生成9位数的qq # ''join()用于字符串连接操作 author.qq = ''.join( str(random.choice(range(1,10))) for _ in range(9) ) print("author.qq=",author.qq) author.addr = 'addr_%s' % (random.randrange(1, 3)) print("author.addr=",author.addr) author.email = '%s@csii.com.cn' % (author.addr) print("author.email=",author.email) author.save() def create_article_and_tags(): # 随机生成文章 for article_title in article_title_list: # 从文章标题中得到tag # split()就是将一个字符串分隔成多个字符串组成的列表 # 已空格为分隔符,只分隔一次,从第一个空格进行分隔 tag_name = article_title.split(' ', 1)[0] print("tag_name = ",tag_name) tag, created = Tag.objects.get_or_create(name=tag_name) random_author = random.choice(Author.objects.all()) print('random_author= ',random_author) for i in range(1, 21): title = '%s_%s' % (article_title, i) print('title',title) article, created = Article.objects.get_or_create( title=title, defaults={ 'author': random_author, #随机分配作者 'context': '%s 正文' %(title), 'score': random.randrange(70, 101)} #随机给文章打分 ) article.tags.add(tag) def main(): create_authors() create_article_and_tags() if __name__ == '__main__': main() print("done")
(csiiDjango) MacBook-Pro-2:zqxt_blog wufq$ python initdb.py
done
导入数据后,我们确认数据是不是真的导入了
>>> from blog.models import Article, Author, Tag >>> Author.objects.all() <QuerySet [<Author: wufuqiang>, <Author: test005>, <Author: jinjin>, <Author: xue>, <Author: wei>]> >>> Tag.objects.all() <QuerySet [<Tag: Django教程>, <Tag: Python教程>, <Tag: HTML教程>]> >>> Article.objects.all() <QuerySet [<Article: Django教程_1>, <Article: Django教程_2>, <Article: Django教程_3>, <Article: Django教程_4>, <Article: Django教程_5>, <Article: DjanArticle: Django教程_7>, <Article: Django教程_8>, <Article: Django教程_9>, <Article: Django教程_10>, <Article: Django教程_11>, <Article: Django教程_12>Django教程_13>, <Article: Django教程_14>, <Article: Django教程_15>, <Article: Django教程_16>, <Article: Django教程_17>, <Article: Django教程_18>, <Art教程_19>, <Article: Django教程_20>, '...(remaining elements truncated)...']> >>>
以上都是准备阶段,已经结束
正式开始学习本节课,学习一些比较高级的查询方法
1、查看Django queryset 执行的SQL
>>> print(Author.objects.all().query)
SELECT "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_author"
简化一下,就是:SELECT id,name,qq,addr,email FROM blog_author
>>> print(Author.objects.filter(name="wufuqiang").query)
SELECT "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_author" WHERE "blog_author"."name" = wufuqiang
简化一下就是:SELECT id, name, qq, addr, email FROM blog_author WHERE name=wufuqiang;
所以,当不知道Django做了什么时,你可以把执行的SQL打出来看看,也可以借助https://github.com/jazzband/django-debug-toolbar 等工具在页面上看到访问当前页面执行了那些SQL,耗时等。
还有一种办法就是修改一下log的设置,后面会讲到。
2、values_list获取元组形式结果
2.1 比如我们要获取作者的name和qq
>>> authors = Author.objects.values_list('name','qq','addr')
>>> authors
<QuerySet [('wufuqiang', '263621251', 'addr_1'), ('test005', '437644648', 'addr_1'), ('jinjin', '394793579', 'addr_2'), ('xue', '712858356', 'addr_2'), ('wei', '743657397', 'addr_1')]>
>>> list(authors)
[('wufuqiang', '263621251', 'addr_1'), ('test005', '437644648', 'addr_1'), ('jinjin', '394793579', 'addr_2'), ('xue', '712858356', 'addr_2'), ('wei', '743657397', 'addr_1')]
如果只需要1个字段,可以指定flat = True
>>> Author.objects.values_list('name',flat=True)
<QuerySet ['wufuqiang', 'test005', 'jinjin', 'xue', 'wei']>
>>> list(Author.objects.values_list('name',flat=True))
['wufuqiang', 'test005', 'jinjin', 'xue', 'wei']
2.2查询wufuqiang这个人的文章标题
>>> Article.objects.filter(author__name='wufuqiang').values_list('title',flat=True)
<QuerySet ['Django教程_1', 'Django教程_2', 'Django教程_3', 'Django教程_4', 'Django教程_5', 'Django教程_6', 'Django教程_7', 'Django教程_8', 'Django教程, 'Django教程_11', 'Django教程_12', 'Django教程_13', 'Django教程_14', 'Django教程_15', 'Django教程_16', 'Django教程_17', 'Django教程_18', 'Django教程_]>
3、values获取字典形式的结果
3.1 比如我们要获取作者的name和qq
>>> authors = Author.objects.values('name','qq')
>>> authors
<QuerySet [{'name': 'wufuqiang', 'qq': '263621251'}, {'name': 'test005', 'qq': '437644648'}, {'name': 'jinjin', 'qq': '394793579'}, {'name': 'xue', 'qq': '712858356'}, {'name': 'wei', 'qq': '743657397'}]>
>>> list(authors)
[{'name': 'wufuqiang', 'qq': '263621251'}, {'name': 'test005', 'qq': '437644648'}, {'name': 'jinjin', 'qq': '394793579'}, {'name': 'xue', 'qq': '712858356'}, {'name': 'wei', 'qq': '743657397'}]
3.2 查询wufuqiang这个人的文章标题
>>> Article.objects.filter(author__name='wufuqiang').values('title')
<QuerySet [{'title': 'Django教程_1'}, {'title': 'Django教程_2'}, {'title': 'Django教程_3'}, {'title': 'Django教程_4'}, {'title': 'Django教程_5'}, {'tingo教程_6'}, {'title': 'Django教程_7'}, {'title': 'Django教程_8'}, {'title': 'Django教程_9'}, {'title': 'Django教程_10'}, {'title': 'Django教程_11'}, jango教程_12'}, {'title': 'Django教程_13'}, {'title': 'Django教程_14'}, {'title': 'Django教程_15'}, {'title': 'Django教程_16'}, {'title': 'Django教程_e': 'Django教程_18'}, {'title': 'Django教程_19'}, {'title': 'Django教程_20'}]>
注意:
1、values_list和values返回的并不是真正的列表或字典,也是queryset,他们也是lazyevaluation的(懒惰评估,通俗地说,就是用的时候才真正的去数据库查)
2、如果查询后没有使用,在数据库更新会在使用,你发现得到在新内容!!!如果想要就内容保持着,数据库更新后不要变,可以list一下
3、如果只是遍历这些结果,没有必要list它们转成列表(浪费内存,数据量大的时候要更谨慎!!!)
4、extra实现别名,条件,排序等
extra中可实现别名,条件,排序等,后面两个用filter,exclude一般都能实现,排序用order__by也能实现。我们主要看一下别名这个
比如Author中有name,Tag中有name我们想执行
SELECT name AS tag_name from blog_tag;
这样的语句,就可以用select来实现,如下:
>>> tags = Tag.objects.all().extra(select={'tag_name':'name'})
>>> tags[0].name
'Django教程'
>>> tags[2].name
'HTML教程'
我们发现name和tag_name都可以使用,确认一下执行的sql
>>> Tag.objects.all().extra(select={'tag_name':'name'}).query.__str__()
'SELECT (name) AS "tag_name", "blog_tag"."id", "blog_tag"."name" FROM "blog_tag"'
我们发现查询的时候弄了两次(name)AS "tag_name"和“blog_tag”."name",
如果我们只想要其中的一个,可以用defer拍出原来的name
>>> Tag.objects.all().extra(select={'tag_name':'name'}).defer('name').query.__str__()
'SELECT (name) AS "tag_name", "blog_tag"."id" FROM "blog_tag"'
也许你会说为什么要改个名称,最常见的需求就是数据转变成 list,然后可视化等,我们在下面一个里面讲
5.annotate聚合计数,求和,平均数等
5.1计数
我们来计算一下每个作者的文章数(我们每个作者都导入的Article的篇数一样,所有下面的每个都一样)
>>> from django.db.models import Count
>>>Article.objects.all().values('author_id').annotate(count=Count('author')).values('author_id','count')
<QuerySet [{'author_id': 1, 'count': 20}, {'author_id': 3, 'count': 20}, {'author_id': 4, 'count': 20}]>
语句分析:
|-- Article.objects.all().values('author_id'):获取文章的作者ID字典
|-- annotate(count=Count('author')):对author进行计数
|-- values('author_id','count'):字典输出的格式:author_id:count
怎么工作的,转换成SQL语句
>>>Article.objects.all().values('author_id').annotate(count=Count('author')).values('author_id','count').query.__str__()
'SELECT "blog_article"."author_id", COUNT("blog_article"."author_id") AS "count" FROM "blog_article" GROUP BY "blog_article"."author_id"'
简化一下SQL:SELECT author_id, COUNT(author_id) AS count FROM blog_article GROUP BY author_id
我们也可以获取作者的名称及作者的文章数
>>>Article.objects.all().values('author__name').annotate(count=Count('author')).values('author__name','count')
<QuerySet [{'author__name': 'jinjin', 'count': 20}, {'author__name': 'wufuqiang', 'count': 20}, {'author__name': 'xue', 'count': 20}]>
>>>Article.objects.all().values('author__name').annotate(count=Count('author')).values('author__name','count').query.__str__()
'SELECT "blog_author"."name", COUNT("blog_article"."author_id") AS "count" FROM "blog_article" INNER JOIN "blog_author" ON ("blog_article"."author_id" = "blog_author"."id") GROUP BY "blog_author"."name"'
这时候实际上查询两张表,因为作者名称(author__name)在 blog_author 这张表中,而上一个例子中的 author_id 是 blog_article 表本身就有的字段