Django模型类查询的惰性求值

Posted hxsen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django模型类查询的惰性求值相关的知识,希望对你有一定的参考价值。

以下是我对django模型类的一些探索,我原本是做php开发的,所以在接触django的模型类的时候,多多少少受到了固有思维的影响。现在把我的探索记录下来,也分享给大家。如有问题,还请多多指正。
基础代码如下:

def test(request):
    headArticle = Article.objects.order_by(‘?‘)[:2]

    return HttpResponse(‘finished‘)

同时,我使用mysql的查询日志记录,想知道什么时候,网站访问了数据库。

模型类的使用

  • 模型类只有在使用的时候,才会去查询数据库
    单独的赋值,没有操作的,也没有执行查询。真正当该数据被使用的时候,才是去计算,去求值。这也是惰性求值的特点。
    如下的代码是没有查询操作的。
    def test(request):
        headArticle = Article.objects.order_by(‘?‘)[:2]
    
        return HttpResponse(‘finished‘)
    
    当我把代码内部,写入一个print操作的时候,此时headArticle才真正被使用到,数据库才真正去查询数据。
    def test(request):
        headArticle = Article.objects.order_by(‘?‘)[:2]
        print(headArticle)
        return HttpResponse(‘finished‘)
    
  • 同一个模型对象,多次调用,会多次查询。
    def test(request):
        headArticle = Article.objects.order_by(‘?‘)[:2]
        print(headArticle)
        print(headArticle)
        return HttpResponse(‘finished‘)
    
    像上面这样,打印两次headArticle,会造成网站对数据库查询两次,因为我使用随机结果,所以,从打印的结果,就能看出来,同样的一个变量,两次打印,有不同的结果。
    [15/May/2020 18:35:41] "GET /test HTTP/1.1" 200 8
    <QuerySet [<Article: mysql对数据结果的前置和后置补0以及判断值的长度>, <Article: Laravel-admin的安装和使用>]>
    <QuerySet [<Article: 三分钟了解PHP的进程和线程>, <Article: laravel中处理集合的函数>]>
    
    这也算是python语言的一个特点吧。所以,使用模型类的时候,注意不要滥用,像这种查询尽量就使用一次,避免造成不必要的多次查询。

filter和get的区别

    qs1 = Article.objects.get(pk=1)
    qs2 = Article.objects.filter(id=2)

get 不会等待,直接出结果。
filter 则会在结果有使用的时候,进行查询。

此时运行的时候,qs1直接查询了数据库,而qs2并未查询。

union的理解

这是官方给出的示例。

>>> qs1 = Author.objects.values_list(‘name‘)
>>> qs2 = Entry.objects.values_list(‘headline‘)
>>> qs1.union(qs2).order_by(‘name‘)

当时,我还好奇,都查出结果了,再联合,岂不是消耗性能?现在看来,是自己想多了。

def test(request):
    qs1 = Article.objects.filter(id=1)
    qs2 = Article.objects.filter(id=2)
    result = qs1.union(qs2)
    
    print(result)

    return HttpResponse(‘finished‘)

如上代码,qs1qs2并未进行查询操作。即便是result变量做联合的时候,也没有进行数据查询。在print打印result之后,才会做真正的查询操作。
下面是查询的语句,是一个联合查询语句,sql内容如下:

(
	SELECT
		`blog_article`.`id`,
		
		...
		
		`blog_article`.`updated_at`
	FROM
		`blog_article`
	WHERE
		`blog_article`.`id` = 1
)
UNION
	(
		SELECT
			`blog_article`.`id`,
			
			...
			
			`blog_article`.`updated_at`
		FROM
			`blog_article`
		WHERE
			`blog_article`.`id` = 2
	)
LIMIT 21

可以看出,djangounionlaravel还是有区别的。在django中大胆使用union就行了,只要使用合理,就不用担心它查询次数太多。

Django分页

  1. 如果,你看到这里 ,也就明白了,为啥django分页的存在了。下面是对结果以每页5个数量,来进行分页。

    def test(request):
        list = Article.objects.all
        paginator = Paginator(list, 5)
    	print(paginator)
    	
        return HttpResponse(‘finished‘)
    

    同样的话,此时的数据库查询,包括分页器操作,都并未执行。
    即便是加入print语句,他的打印结果,也只是对象名,此时并未查询数据库,因为真正的结果集并未使用。
    什么时候查询呢?不急,慢慢来

  2. 当我们获取某一页数据的时候,比如下面的这种情况,会怎样呢?

    def test(request):
        list = Article.objects.all()
        paginator = Paginator(list, 5)
    
        page_obj = paginator.get_page(2)
        print(page_obj)
        
        return HttpResponse(‘finished‘)
    

    此时,开始查询数据库了。
    命令行输出的是,分页的信息。可以看到是个yield生成器。

    UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class ‘blog.models.Article‘> QuerySet.
      paginator = Paginator(list, 5)
    <Page 2 of 44>
    

    但是这里,仅仅执行了查询总数的sql语句。

    SELECT COUNT(*) AS `__count` FROM `blog_article`
    
  3. 当我真正的想读取分页的内容的时候,此时才是真正的数据列表查询开始,像下面的这样

    def test(request):
        list = Article.objects.all()
        paginator = Paginator(list, 5)
    
        page_obj = paginator.get_page(2)
        print(page_obj.object_list)
    
        return HttpResponse(‘finished‘)
    

    此时查询的语句如下:

    SELECT `blog_article`.`id`,  ...  `blog_article`.`updated_at` FROM `blog_article`  LIMIT 5 OFFSET 5
    

    可以看到,它已经成功个了对sql语句加入的限制条件。可以看出来,django的分页计算确实高明,能正确的算出你想要的数据,你分页的时候,就可以考虑直接使用了。有关更详细的操作,建议参照django官方文档-分页

另外:更多关于yield生成器的使用方法,请参考官方文档-生成器,或者关注我后期的对yield的讲解。









以上是关于Django模型类查询的惰性求值的主要内容,如果未能解决你的问题,请参考以下文章

惰性求值和 const 正确性问题

从属属性中的 MATLAB 惰性求值

python 短路求值或惰性求值

C#函数式编程中的惰性求值详解

Python惰性求值器

Stream01 定义迭代操作惰性求值