zoukankan      html  css  js  c++  java
  • 【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 表本身就有的字段



  • 相关阅读:
    广域网(ppp协议、HDLC协议)
    0120. Triangle (M)
    0589. N-ary Tree Preorder Traversal (E)
    0377. Combination Sum IV (M)
    1074. Number of Submatrices That Sum to Target (H)
    1209. Remove All Adjacent Duplicates in String II (M)
    0509. Fibonacci Number (E)
    0086. Partition List (M)
    0667. Beautiful Arrangement II (M)
    1302. Deepest Leaves Sum (M)
  • 原文地址:https://www.cnblogs.com/frankruby/p/10880403.html
Copyright © 2011-2022 走看看