Loading

[转] 高效使用 django 的 queryset

1. DjangoQuerySet 是惰性的

DjangoQuerySet 对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得到数据库中名字为 Dave 的所有的人:

person_set = Person.objects.filter(first_name="Dave")

上面的代码并没有运行任何的数据库查询。你可以使用 person_set,给它加上一些过滤条件,或者将它传给某个函数,这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响 web 应用性能的因素之一。


2. 真正从数据库获得数据

要真正从数据库获得数据,你可以遍历 QuerySet 或者使用 if QuerySet ,总之你用到数据时就会执行 sql。为了验证这些,需要在 settings 里加入 LOGGING,验证方式:

obj=models.Book.objects.filter(id=3)
# for i in obj:
#     print(i)

# if obj:
#     print("ok")

3. QuerySet 是具有 cache

当你遍历 QuerySet 时,所有匹配的记录会从数据库获取,然后转换成 Djangomodel。这被称为执行(evaluation)。这些 model 会保存在 QuerySet 内置的 cache 中,这样如果你再次遍历这个 QuerySet,你不需要重复运行通用的查询。

obj=models.Book.objects.filter(id=3)
# for i in obj:
#     print(i)

## models.Book.objects.filter(id=3).update(title="GO")
## obj_new=models.Book.objects.filter(id=3)

# for i in obj:
#     print(i)   #LOGGING只会打印一次

4. 验证 cache 中是否有数据

简单的使用 if 语句进行判断也会完全执行整个 QuerySet 并且把数据放入 cache,虽然你并不需要这些数据!为了避免这个,可以用 exists() 方法来检查是否有数据:

obj = Book.objects.filter(id=4)
#  exists()的检查可以避免数据放入queryset的cache。
if obj.exists():
    print("hello world!")

5. 当 QuerySet 非常巨大时,cache 会成为问题

处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的 QuerySet 可能会锁住系统进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生 QuerySet cache,可以使用 iterator() 方法来获取数据,处理完数据就将其丢弃。

objs = Book.objects.all().iterator()
# iterator()可以一次只从数据库获取少量数据,这样可以节省内存
for obj in objs:
    print(obj.name)
#BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了
for obj in objs:
    print(obj.name)

当然,使用 iterator() 方法来防止生成 cache,意味着遍历同一个 QuerySet 时会重复执行查询。所以使用 iterator() 的时候要当心,确保你的代码在操作一个大的 QuerySet 时没有重复执行查询。


6. 总结

QuerySetcache 是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。使用 exists()iterator() 方法可以优化程序对内存的使用。不过,由于它们并不会生成 QuerySet cache,可能会造成额外的数据库查询。

posted @ 2024-06-25 22:26  kingron  阅读(7)  评论(0编辑  收藏  举报