Django的QuerySets

对象关系映射 (ORM) 使得与SQL数据库交互更为简单,不过也被认为效率不高,比原始的SQL要慢。
要有效的使用ORM,意味着需要多少要明白它是如何查询数据库的。本文我将重点介绍如何有效使用 Django ORM系统访问中到大型的数据集。

##Django的queryset是惰性的

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

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

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

for person in person_set:
    print(person.last_name)

##Django的queryset是具有cache的

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

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是否包含数据,只有有数据时才会去遍历:

restaurant_set = Restaurant.objects.filter(cuisine="Indian")
# `if`语句会触发queryset的执行。
if restaurant_set:
    # 遍历时用的是cache中的数据
    for restaurant in restaurant_set:
        print(restaurant.name)

##QuerySet的常用API

###去重
distinct方法的作用和SQL的distinct的作用是一样的,这里就不多说了

Author.objects.distinct()

###序列化为JSON
和前端进行交互的时候,序列化成XML的情况还是比较少的,但是序列化成JSON格式的字符串的情况就比较多了,Django的ORM框架提供了values的方法来把实体序列化成json

Blog.objects.values()

###序列化成数组
把对象序列化成数组元素的情况还真的是用的比较少

Entry.objects.values_list('id', 'headline')

###从主表获取外键表的对象
在主表的实体上使用filter,然后再通过主表的实体获取外键的时候,Django会把主表的实体一并查询出来。但是有时候我们只希望从主表开始取数,但是只取外键表实体的信息,这个时候就可以用select_related方法了。第一个参数是主表的外键字段名称,第二个字段嘛。。。照抄就是了

g = Group.objects.select_related('room', 'subject')

当然,也会有只希望拿主表信息,不想把外键字段取回来的情况,把参数设置为none就好了

without_relations = queryset.select_related(None)

###处理从表对象
对于外键表来说,我们可以采用select_related的方法来进行处理。但是对于从表属性来说,就要用到另外一种方法了。(toppings是多对多的属性)

Pizza.objects.all().prefetch_related('toppings')

同样的,不获取外键对象的话把参数设置为none就好了

non_prefetched = qs.prefetch_related(None)

###ORM和SQL的混合:Extra方法
先看看Extra方法的一些例子

####selec参数
Entry.objects.extra(select={‘is_recent’: “pub_date > ‘2006-01-01’”})

####where参数
Entry.objects.extra(where=[“foo=’a’ OR bar = ‘a’”, “baz = ‘a’”])

####order_by参数
q = q.extra(order_by = [‘-is_recent’])

####paramas参数
Entry.objects.extra(where=[‘headline=%s’], params=[‘Lennon’])
其实从官方给的例子看起来,Extra方法主要就是用于在ORM生成SQL的过程中内嵌SQL语句。例如希望在select 背后加入一句sql,例如select …,(select a from …)这种情况下的时候,用Extra方法就很好解决了

###使用Defer和Only来过滤字段
select_related和prefetch_related方法分别用来过滤实体的外键属性和多对多属性,对于主表的字段属性过滤可以采用defer方法来少查询一些字段

Entry.objects.defer("headline", "body")

defer是排除哪些字段,而only是只查询哪些字段

Person.objects.only("name")

###使用select_for_update来简化更新的过程
一般更新实体的时候,我们需要先把实体先查询出来,然后做出相应的更新,再做一次save操作

entity=Entry.objects.filter(...)[0]
entity.name=xx
entity.save()

这个过程显得稍微有点繁琐,所以可以采用一种select_for_update来进行一些简化

entries = Entry.objects.select_for_update().filter(author=request.user)

###使用get_or_create来简化创建对象的过程
同样的,有时候我们会需要先查询对象在不在数据库里面存储,假如没有的话就创建,有的话就取出

obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon',
              defaults={'birthday': date(1940, 10, 9)})

不过个人觉得这个方法用到的情况很少。。。。

###批量插入数据
Entry.objects.bulk_create([
Entry(headline=”Django 1.0 Released”),
Entry(headline=”Django 1.1 Announced”),
Entry(headline=”Breaking: Django is awesome”)
])
这玩意真的挺有用,具体的就不细说了

###使用Count方法来进行统计
虽然做统计的话可以先把过滤完的对象查询出来,然后再len(xx)一下,但是这种做法会把数据库里面一堆一堆的暑假都查出来的( ̄◇ ̄;),所以当需要做count操作的时候还是老老实实的用sql的count语句,在Django里面的话做法如下

Entry.objects.filter(headline__contains='Lennon').count()

###判断数据是否存在:exists
rs=some_queryset.filter(pk=entry.pk).exists()