有效使用Django的QuerySets
对象关系映射 (ORM) 使得与SQL数据库交互更为简单,不过也被认为效率不高,比原始的SQL要慢。
要有效的使用ORM,意味着需要多少要明白它是如何查询数据库的。本文我将重点介绍如何有效使用 Django ORM系统访问中到大型的数据集。
Django的queryset是惰性的
Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得到数据库中名字为‘Dave’的所有的人:
1
|
person_set = Person.objects. filter (first_name = "Dave" ) |
上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数,这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。
要真正从数据库获得数据,你需要遍历queryset:
1
2
|
for person in person_set: print (person.last_name) |
Django的queryset是具有cache的
当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行(evaluation)。这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,你不需要重复运行通用的查询。
例如,下面的代码只会执行一次数据库查询:
1
2
3
4
5
6
7
|
pet_set = Pet.objects. filter (species = "Dog" ) # The query is executed and cached. for pet in pet_set: print (pet.first_name) # The cache is used for subsequent iteration. for pet in pet_set: print (pet.last_name) |
if语句会触发queryset的执行
queryset的cache最有用的地方是可以有效的测试queryset是否包含数据,只有有数据时才会去遍历:
1
2
3
4
5
6
|
restaurant_set = Restaurant.objects. filter (cuisine = "Indian" ) # `if`语句会触发queryset的执行。 if restaurant_set: # 遍历时用的是cache中的数据 for restaurant in restaurant_set: print (restaurant.name) |
如果不需要所有数据,queryset的cache可能会是个问题
有时候,你也许只想知道是否有数据存在,而不需要遍历所有的数据。这种情况,简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些数据!
1
2
3
4
5
|
city_set = City.objects. filter (name = "Cambridge" ) # `if`语句会执行queryset.。 if city_set: # 我们并不需要所有的数据,但是ORM仍然会获取所有记录! print ( "At least one city called Cambridge still stands!" ) |
为了避免这个,可以用exists()方法来检查是否有数据:
1
2
3
4
5
|
tree_set = Tree.objects. filter ( type = "deciduous" ) # `exists()`的检查可以避免数据放入queryset的cache。 if tree_set.exists(): # 没有数据从数据库获取,从而节省了带宽和内存 print ( "There are still hardwood trees in the world!" ) |
当queryset非常巨大时,cache会成为问题
处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统进程,让你的程序濒临崩溃。
要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法来获取数据,处理完数据就将其丢弃。
1
2
3
4
|
star_set = Star.objects. all () # `iterator()`可以一次只从数据库获取少量数据,这样可以节省内存 for star in star_set.iterator(): print (star.name) |
当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询
如果查询集很大的话,if 语句是个问题
如前所述,查询集缓存对于组合 if 语句和 for 语句是很强大的,它允许在一个查询集上进行有条件的循环。然而对于很大的查询集,则不适合使用查询集缓存。
最简单的解决方案是结合使用exists()和iterator(), 通过使用两次数据库查询来避免使用查询集缓存。
1
2
3
4
5
6
|
molecule_set = Molecule.objects. all () # One database query to test if any rows exist. if molecule_set.exists(): # Another database query to start fetching the rows in batches. for molecule in molecule_set.iterator(): print (molecule.velocity) |
一个更复杂点的方案是使用 Python 的“ 高级迭代方法 ”在开始循环前先查看一下 iterator() 的第一个元素再决定是否进行循环。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
atom_set = Atom.objects. all () # One database query to start fetching the rows in batches. atom_iterator = atom_set.iterator() # Peek at the first item in the iterator. try : first_atom = next (atom_iterator) except StopIteration: # No rows were found, so do nothing. pass else : # At least one row was found, so iterate over # all the rows, including the first one. from itertools import chain for atom in chain([first_atom], atom_set): print (atom.mass) |
防止不当的优化
queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。
使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能会造成额外的数据库查询。
所以编码时需要注意一下,如果程序开始变慢,你需要看看代码的瓶颈在哪里,是否会有一些小的优化可以帮到你。
英文原文:Using Django querysets effectively
有效使用Django的QuerySets的更多相关文章
- django book querysets
from __future__ import unicode_literals from django.db import models from django.contrib.auth.models ...
- Django——QuerySets酷毙了!
Django的QuerySets酷毙了! 在本文中我将解释一下QuerySets是什么,它是如何工作的(如果你对它已经熟悉了,你可以直接跳到第二部分),我认为如果可以的话你应该总是返回QuerySet ...
- django性能优化
1. 内存.内存,还是加内存 2. 使用单独的静态文件服务器 3. 关闭KeepAlive(如果服务器不提供静态文件服务,如:大文件下载) 4. 使用memcached 5. 使用select_rel ...
- Django 数据库查询优化
Django数据层提供各种途径优化数据的访问,一个项目大量优化工作一般是放在后期来做,早期的优化是“万恶之源”,这是前人总结的经验,不无道理.如果事先理解Django的优化技巧,开发过程中稍稍留意,后 ...
- Django的性能优化
Django的性能优化 一,利用标准数据库优化技术 传统数据库优化技术博大精深,不同的数据库有不同的优化技巧,但重心还是有规则的.在这里算是题外话,挑两点通用的说说: 索引,给关键的字段添加索引, ...
- Django : Security in Django
Security in Django https://docs.djangoproject.com/en/1.10/topics/security/ 1 Cross site scripting (X ...
- Django中不返回QuerySets的API -- Django从入门到精通系列教程
该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...
- django不返回QuerySets的API
以下的方法不会返回QuerySets,但是作用非常强大,尤其是粗体显示的方法,需要背下来. 方法名 解释 get() 获取单个对象 create() 创建对象,无需save() get_or_crea ...
- Paginator Django 分页 When QuerySets are evaluated QuerySets 执行原理 QuerySets are lazy 惰性执行 访问db取数据的时机
https://docs.djangoproject.com/en/2.2/topics/pagination/ Paginator objects¶ The Paginator class has ...
随机推荐
- VS2008配合SQLite开发WINCE、PDA智能设备项目环境搭设。
1.安装vs2008 ---------------------------vs2008上安装TFS步骤(详细请见——http://www.cnblogs.com/mayt/archive/2013/ ...
- MyBatis中<![CDATA[ ]]>的使用
原文地址:https://www.cnblogs.com/catgatp/p/6403382.html <![CDATA[]]>和转义字符 被<![CDATA[]]>这个标记所 ...
- CentOS7 安装 RocketMQ 实践和小示例
CentOS7 安装 RocketMQ 实践和小示例 1.通过 SSH 工具(比如 XShell)连接到 CentOS7 服务器上: 2.进入到 /usr/local 目录中: cd /usr/loc ...
- Kafka、RabbitMQ、RocketMQ等消息中间件的介绍和对比(转)
前言在分布式系统中,我们广泛运用消息中间件进行系统间的数据交换,便于异步解耦.现在开源的消息中间件有很多,前段时间产品 RocketMQ (MetaQ的内核) 也顺利开源,得到大家的关注. 概念MQ简 ...
- Springboot项目报错【java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader】
1.发生问题: 升级了JDK9,发现原先的springboot项目起不来了,以为是maven中jdk配置有问题. 于是在pom中添加了 <plugin> <groupId>or ...
- python学习_新闻联播文字版爬虫(V 1.0版)
python3的爬虫练习,爬取的是新闻联播文字版网站 #!/usr/bin/env python # -*- coding: utf-8 -*- ''' __author__ = 'wyf349' _ ...
- redis异步处理
$reids = new Redis; $redis->connect('localhost',6379); $redis->auth(''); //将数组转换成字符串再存到redis中 ...
- 在py文件中运行flask cli命令
主要是打包成exe之后,用于运行命令行. 命令行是 @app.cli.command() def mock(): ... 这样定义的 本来是用flask mock 这样方式运行的 打包后,希望用 ap ...
- ACM之路(19)—— 主席树初探
长春赛的 I 题是主席树,现在稍微的学了一点主席树,也就算入了个门吧= = 简单的来说主席树就是每个节点上面都是一棵线段树,但是这么多线段树会MLE吧?其实我们解决的办法就是有重复的节点给他利用起来, ...
- arxiv-sanity使用指南
使用介绍 https://bookdown.org/wshuyi/intro-to-scientific-writings4/reading.html#find-article-with-ai