• 【Django QuerySet进阶 010】


    阅读本文你可以学习到什么

    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 表本身就有的字段



  • 相关阅读:
    SpringKafka——消息监听
    RabbitMQ(八)线程池消费
    RabbitMQ(七)延迟队列
    window计划任务 0x1
    获取网页URL地址及参数等的两种方法(js和C#)
    HttpWebRequest的偶尔请求超时问题
    用HTML、CSS、JS制作圆形进度条(无动画效果)
    批量关联update
    仅仅 IE8 有效的 CSS hack 写法
    SqlServer关闭与启用标识(自增长)列
  • 原文地址:https://www.cnblogs.com/frankruby/p/10880403.html
Copyright © 2020-2023  润新知