一个查询的创建并不会访问数据库,直到获取这条查询语句的具体数据的时候,系统才会去访问数据库:
- def t7(request):
- # 不查询数据库,只要是首次返回queryset,都会受懒加载的影响,不去查询数据库
- models.Text_one.objects.filter(name='xx')
- # 查询数据库
- data = models.Text_one.objects.filter(name='xx')[0:10]
- return HttpResponse('ok')
迭代、使用步长分片、len(),list()数据库才会被查询
每一个 QuerySet 都会有一个缓存来减少对数据库的访问操作,理解其中的运行原理能帮助我们写出最有效的代码。
当我们创建一个 QuerySet 的之后,并且数据第一次被加载,对数据库的查询操作就发生了。
然后 Django 会保存 QuerySet 查询的结果,并且在之后对这个 QuerySet 的操作中会重复使用,不会再去查询数据库。
- # orm
- def t7(request):
- # 不查询数据库,只要是首次返回queryset,都会受懒加载的影响,不去查询数据库
- data_query = models.Text_one.objects.filter(name='xx')
- # 只查询一次数据库
- a = [item['name'] for item in data_query]
- # 每次循环都查询数据库
- b = [item['name'] for item in models.Text_one.objects.filter(name='xx')]
- return HttpResponse('ok')
比如下面外键字段的获取,userinfo是 Text_one的一个外键字段:
- # orm
- def t7(request):
- # 查询数据库
- data_obj = models.Text_one.objects.filter(name='xx').first()
- # 查询数据库
- name1 = data_obj.userinfo
- # 缓存机制,不查询数据库
- name2 = data_obj.userinfo
- return HttpResponse('ok')
而多对多关系的获取,每次都会被重新去数据库获取数据
- # orm
- def t7(request):
- # 查询数据库
- data_obj = models.Text_one.objects.filter(name='xx').first()
- # 查询数据库
- name1 = data_obj.auth.all()
- # 查询数据库
- name2 = data_obj.auth.all()
- return HttpResponse('ok')
1. 在大多数查询中,使用 filter() 和 exclude() 在数据库中做过滤,而不是在获取所有数据之后在 Python 里的 for 循环里筛选数据
2. 在同一个 model 的操作中,如果有涉及到其他字段的操作,可以用到 F 表达式
3. 使用 annotate 函数在数据库中做聚合(aggregate)的操作4. 如果某些查询比较复杂,可以使用原生的 SQL 语句
1. 在使用 get() 来查询单条数据的时候,有两个理由使用唯一索引(unique)或 普通索引(db_index)
- 一个是基于数据库索引,查询会更快,
- 另一个是如果多条数据都满足查询条件,查询会慢得多,而在唯一索引的约束下则保证这种情况不会发生
所以使用下面的 id 进行匹配 会比 name字段匹配快得多,因为 id 字段在数据库中有索引且是唯一的
首先, name字段上没有索引,会导致数据库获取速度慢
其次,查询并不能保证只返回一个对象,如果匹配上来多个对象,且从数据库中检索并返回数百数千条记录,后果会很严重,其实就会报错,get() 能接受的返回只能是一个实例数据。
- def t7(request):
- # 快
- data_obj = models.Text_one.objects.get(id=1)
- # 中
- data_obj = models.Text_one.objects.get(name='xx')
- # 慢
- data_obj = models.Text_one.objects.get(name__startswith='xx')
-
- return HttpResponse('ok')
能一次性查询所有需要的相关的数据的话,就一次性查询出来,不要在循环中做多次查询,因为那样会多次访问数据库
所以这就需要理解并且用到 select_related() 和 prefetch_related() 函数
如果需求仅仅是需要某几个字段的数据,可以用到的数据结构为 dict 或者 list,可以直接使用这两个函数来获取数据
如果明确知道只需要,或者不需要什么字段数据,可以使用这两个方法,一般常用在 textfield 上,避免加载大数据量的 text 字段
如果想要获取总数,使用 count() 方法,而不是使用 len() 来操作,如果数据有一万条,len() 操作会导致这一万条数据都加载到内存里,然后计数。
如果仅仅是想查询数据是否至少存在一条可以使用 if QuerySet.exists() 而不是 if queryset 的形式
能够批量更新和删除的操作就使用批量的方法,挨个去加载数据,更新数据,然后保存是不推荐的
如果需要外键的值,直接调用早就在这个 object 中的字段,而不是加载整个关联的 object 然后取其主键id
每一个字段的排序都是数据库的操作需要额外消耗性能的,所以如果不需要的话,尽量不要排序
如果在 Meta.ordering 中有一个默认的排序,而你不需要,可以通过 order_by() 不添加任何参数的方法来取消排序
为数据库添加索引,可以帮助提高排序的性能
对于多条 model 数据的创建,尽可能的使用 bulk_create() 方法,这是要优于挨个去 create() 的
bulk_update 方法也优于挨个数据在 for 循环中去 save()
对于 ManyToMany 方法,使用 add() 方法的时候添加多个参数一次性操作比多次 add 要好
当去除 ManyToMany 中的数据的时候,也是能一次性操作就一次性操作