django中的queryset对象和object对象详解

 

1. queryset是查询集,就是传到服务器上的url里面的内容。Django会对查询返回的结果集QerySet进行缓存,这里是为了提高查询效率。

    也就是说,在你创建一个QuerySet对象的时候,Django并不会立即向数据库发出查询命令,只有在你需要用到这个QuerySet的时候才回去数据库查询。

2. Objects是django实现的mvc框架中的数据层(model)m,django中的模型类都有一个objects对象,它是一个django中定义的QuerySet类型的对象,

    它包含了模型对象的实例。简单的说,objects是单个对象,queryset是许多对象。

QuerySet 可以被构造,过滤,切片,做为参数传递,这些行为都不会对数据库进行操作。只要你查询的时候才真正的操作数据库。

下面的 QuerySet 行为会导致执行查询的操作:

循环(Iteration):QuerySet 是可迭代的,在你遍历对象时就会执行数据库操作。例如,打印出所有博文的大标题:

1
2
for  in  Entry.objects. all ():
     print (e.headline)

切片(Slicing): QuerySet 是可以用 Python 的数组切片语法完成切片。一般来说对一个未查询的 QuerySet 切片就返回另一个未查询的 QuerySet (新 QuerySet 不会被执行)。不过如果你在切片时使用了 "step" 参数,Django 仍会执行数据库操作并且返回列表对象。对一个查询过的QuerySet执行切片也会返回列表对象。

序列化/缓存化(Pickling/Caching): 详情请查看 pickling QuerySets。 这一节所强调的一点是查询结果是从数据库中读取的。

repr(). 调用 QuerySet 的 repr() 方法时,查询就会被运行。这对于 Python 命令行来说非常方便,你可以使用 API 立即看到查询结果。

len(). 调用 QuerySet 的 len() 方法,查询就会被运行。这正如你所料,会返回查询结果列表的长度。

注意:如果你想得到集合中记录的数量,就不要使用 QuerySet 的 len() 方法。因为直接在数据库层面使用 SQL 的 SELECT COUNT(*) 会更加高效,Django 提供了 count() 方法就是这个原因。详情参阅下面的 count() 方法。

list(). 对 QuerySet 应用 list() 方法,就会运行查询。例如:

1
entry_list  =  list (Entry.objects. all ())

要注意地是:使用这个方法会占用大量内存,因为 Django 将列表内容都载入到内存中。做为对比,遍历 QuerySet 是从数据库读取数据,仅在使用某个对象时才将其载入到内容中。

Pickling QuerySets

如果你要 序列化(pickle) 一个 QuerySet,Django 首先就会将查询对象载入到内存中以完成序列化,这样你就可以第一时间使用对象(直接从数据库中读取数据需要一定时间,这正是缓存所想避免的)。而序列化是缓存化的先行工作,所以在缓存查询时,首先就会进行序列化工作。这意味着当你反序列化 QuerySet 时,第一时间就会从内存中获得查询的结果,而不是从数据库中查找。

如果你只是想序列化部分必要的信息以便晚些时候可以从数据库中重建 Queryset ,那只序列化 QuerySet 的 query 属性即可。接下来你就可以使用下面的代码重建原来的 QuerySet (这当中没有数据库读取):

1
2
3
4
>>>  import  pickle
>>> query  =  pickle.loads(s)      # Assuming 's' is the pickled string.
>>> qs  =  MyModel.objects. all ()
>>> qs.query  =  query             # Restore the original 'query'.

query 属性是一个不透明的对象。这就意味着它的内部结构并不是公开的。即便如此,对于本节提到的序列化和反序列化来说,它仍是安全和被完全支持的。

查询API(QuerySet API)

filter(**kwargs)

返回一个新的 QuerySet ,它包含了与所给的筛选条件相匹配的对象。

这些筛选条件(**kwargs)在下面的字段筛选(Field lookups) 中有详细介绍。多个条件之间在 SQL 语句中是 AND 关系。

exclude(**kwargs)

返回一个新的 QuerySet,它包含那些与所给筛选条件不匹配的对象。

这些筛选条件(**kwargs)也在下面的 字段筛选(Field lookups) 中有详细描述。多个条件之间在 SQL 语句中也是 AND 关系,但是整体又是一个 NOT() 关系。

下面的例子剔除了出版日期 pub_date 晚于 2005-1-3 并且大标题 headline 是 "Hello" 的所有博文(entry):

1
Entry.objects.exclude(pub_date__gt = datetime.date( 2005 1 3 ), headline = 'Hello' )

在 SQL 语句中,这等价于:

1
2
SELECT ...
WHERE NOT (pub_date >  '2005-1-3'  AND headline  =  'Hello' )

下面的例子剔除出版日期 pub_date 晚于 2005-1-3 或者大标题是 "Hello" 的所有博文:

1
Entry.objects.exclude(pub_date__gt = datetime.date( 2005 1 3 )).exclude(headline = 'Hello' )

在 SQL 语句中,这等价于:

1
2
3
SELECT ...
WHERE NOT pub_date >  '2005-1-3'
OR NOT headline  =  'Hello'

要注意第二个例子是有很多限制的。

annotate(*args, **kwargs)

我们可以为 QuerySet 中的每个对象添加注解。可以通过计算查询结果中每个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和,等等),做为 QuerySet 中对象的注解。annotate() 中的每个参数都会被做为注解添加到 QuerySet 中返回的对象。

Django 提供的注解函式在下面的 (注解函式Aggregation Functions) 有详细介绍。

注解会使用关键字参数来做为注解的别名。其中任何参数都会生成一个别名,它与注解函式的名称和被注解的 model 相关。

例如,你正在操作一个博客列表,你想知道一个博客究竟有多少篇博文:

1
2
3
4
5
6
7
8
>>>  from  django.db.models  import  Count
>>> q  =  Blog.objects.annotate(Count( 'entry' ))
# The name of the first blog
>>> q[ 0 ].name
'Blogasaurus'
# The number of entries on the first blog
>>> q[ 0 ].entry__count
42

Blog model 类本身并没有定义 entry__count 属性,但可以使用注解函式的关系字参数,从而改变注解的命名:

1
2
3
4
>>> q  =  Blog.objects.annotate(number_of_entries = Count( 'entry' ))
# The number of entries on the first blog, using the name provided
>>> q[ 0 ].number_of_entries
42

order_by(*fields)

默认情况下, QuerySet 返回的查询结果是根据 model 类的 Meta 设置所提供的 ordering 项中定义的排序元组来进行对象排序的。你可以使用 order_by 方法覆盖之前 QuerySet 中的排序设置。

例如:

1
Entry.objects. filter (pub_date__year = 2005 ).order_by( '-pub_date' 'headline' )

返回结果就会先按照 pub_date 进行升序排序,再按照 headline 进行降序排序。 "-pub_date" 前面的负号"-"表示降序排序。默认是采用升序排序。要随机排序,就使用 "?",例如:

1
Entry.objects.order_by( '?' )

注意:order_by('?') 可能会非常缓慢和消耗过多资源,这取决于你所使用的数据库。

要根据其他 model 字段排序,所用语法和跨关系查询的语法相同。就是说,用两个连续的下划线(__)连接关联 model 和 要排序的字段名称, 而且可以一直延伸。例如:

1
Entry.objects.order_by( 'blog__name' 'headline' )

如果你想对关联字段排序,在没有指定 Meta.ordering 的情况下,Django 会采用默认排序设置,就是按照关联 model 的主键进行排序。例如:

1
Entry.objects.order_by( 'blog' )

等价于:

1
Entry.objects.order_by( 'blog__id' )

这是因为 Blog model 没有声明排序项的原故。

Django 1.7新添加:

1
2
3
4
5
# No Join
Entry.objects.order_by( 'blog_id' )      #可以避免JOIN的代价
 
# Join
Entry.objects.order_by( 'blog__id' )

如果你使用了 distinct() 方法,那么在对关联字段排序时要格外谨慎。

在 Django 当中是可以按照多值字段(例如 ManyToMany 字段)进行排序的。不过,这个特性虽然先进,但是并不实用。除非是你已经很清楚过滤结果或可用数据中的每个对象,都只有一个相关联的对象时(就是相当于只是一对一关系时),排序才会符合你预期的结果,所以对多值字段排序时要格外注意。

如果你不想对任何字段排序,也不想使用 model 中原有的排序设置,那么可以调用无参数的 order_by() 方法。

对于排序项是否应该大小写敏感,Django 并没有提供设置方法,这完全取决于后端的数据库对排序大小写如何处理。

你可以令某个查询结果是可排序的,也可以是不可排序的,这取决于 QuerySet.ordered 属性。如果它的值是 True ,那么 QuerySet 就是可排序的。

reverse()

使用 reverse() 方法会对查询结果进行反向排序。调用两次 reverse() 方法相当于排序没发生改过。

要得到查询结果中最后五个对象,可以这样写:

1
my_queryset.reverse()[: 5 ]

要注意这种方式与 Python 语法中的从尾部切片是完全不一样的。在上面的例子中,是先得到最后一个元素,然后是倒数第二个,依次处理。但是如果我们有一个 Python 队列,使用 seq[-5:]时,却是先得到倒数第五个元素。Django 之所以采用 reverse 来获取倒数的记录,而不支持切片的方法,原因就是后者在 SQL 中难以做好。

还有一点要注意,就是 reverse() 方法应该只作用于已定义了排序项 QuerySet (例如,在查询时使用了order_by()方法,或是在 model 类当中直接定义了排序项). 如果并没有明确定义排序项,那么调用 QuerySet, calling reverse() 就没什么实际意义(因为在调用 reverse() 之前,数据没有定义排序,所以在这之后也不会进行排序。)

distinct()

返回一个新的 QuerySet ,它会在执行 SQL 查询时使用 SELECT DISTINCT。这意味着返回结果中的重复记录将被剔除。

默认情况下, QuerySet 并会剔除重复的记录。在实际当中,这不是什么问题,因为象 Blog.objects.all() 这样的查询并不会产生重复的记录。但是,如果你使用 QuerySet 做多表查询时,就很可能会产生重复记录。这时,就可以使用 distinct() 方法。

Note

在 order_by(*fields) 中出现的字段也会包含在 SQL SELECT 列中。如果和 distinct() 同时使用,有时返回的结果却与预想的不同。这是因为:如果你对跨关系的关联字段进行排序,这些字段就会被添加到被选取的列中,这就可能产生重复数据(比如,其他的列数据都相同,只是关联字段的值不同)。但由于 order_by 中的关联字段并不会出现在返回结果中(他们仅仅是用来实现order),所以有时返回的数据看上去就象是并没有进行过 distinct 处理一样。

同样的原因,如果你用 values() 方法获得被选取的列,就会发现包含在 order_by() (或是 model 类的 Meta 中设置的排序项)中的字段也包含在里面,就会对返回的结果产生影响。

这章节强调的就是在你使用 distinct() 时,要谨慎对待关联字段排序。同样的,在同时使用 distinct() 和 values() 时,如果排序字段并没有出现在 values() 返回的结果中,那么也要引起注意。

values(*fields)

返回一个 ValuesQuerySet ----一个特殊的 QuerySet ,运行后得到的并不是一系列 model 的实例化对象,而是一个可迭代的字典序列。

每个字典都表示一个对象,而键名就是 model 对象的属性名称。

下面的例子就对 values() 得到的字典与传统的 model 对象进行了对比:

1
2
3
4
5
6
7
# This list contains a Blog object.
>>> Blog.objects. filter (name__startswith = 'Beatles' )
[<Blog: Beatles Blog>]
 
# This list contains a dictionary.
>>> Blog.objects. filter (name__startswith = 'Beatles' ).values()
[{ 'id' 1 'name' 'Beatles Blog' 'tagline' 'All the latest Beatles news.' }]

values() 可以接收可选的位置参数,*fields,就是字段的名称,用来限制 SELECT 选取的数据。如果你指定了字段参数,每个字典就会以 Key-Value 的形式保存你所指定的字段信息;如果没有指定,每个字典就会包含当前数据表当中的所有字段信息。

例如:

1
2
3
4
>>> Blog.objects.values()
[{ 'id' 1 'name' 'Beatles Blog' 'tagline' 'All the latest Beatles news.' }],
>>> Blog.objects.values( 'id' 'name' )
[{ 'id' 1 'name' 'Beatles Blog' }]

下面这些细节值得注意:

    如果你有一个名为 foo 的ForeignKey 字段,默认情况下调用 values() 返回的字典中包含键名为 foo_id 的字典项,因为它是一个隐含的 model 字段,用来保存关联对象的主键值( foo 属性用来联系相关联的 model )。当你使用 values() 并传递字段名称时, 传递foo 或 foo_id 都会得到相同的结果 (字典中的键名会自动换成你传递的字段名称)。

例如:

1
2
3
4
5
6
7
8
>>> Entry.objects.values()
[{ 'blog_id' 1 'headline' : u 'First Entry' , ...}, ...]
 
>>> Entry.objects.values( 'blog' )
[{ 'blog' 1 }, ...]
 
>>> Entry.objects.values( 'blog_id' )
[{ 'blog_id' 1 }, ...]

    在 values() 和 distinct() 同时使用时,要注意排序项会影响返回的结果,详情请查看上面 distinct() 一节。

    在values()之后使用defer()和only()是无用的。

ValuesQuerySet 是非常有用的。利用它,你就可以只获得你所需的那部分数据,而不必同时读取其他的无用数据。

最后,要提醒的是,ValuesQuerySet 是 QuerySet 的一个子类,所以它拥有 QuerySet 所有的方法。你可以对它调用 filter() 或是 order_by() 以及其他方法。所以下面俩种写法是等价的:

1
2
Blog.objects.values().order_by( 'id' )
Blog.objects.order_by( 'id' ).values()

Django 的编写者们更喜欢第二种写法,就是先写影响 SQL 的方法,再写影响输出的方法(比如例中先写 order,再写values ),但这些都无关紧要,完全视你个人喜好而定。

也可以指向一对一、多对多、外键关系对象的域:

1
2
3
Blog.objects.values( 'name' 'entry__headline' ) </
  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值