介绍Python的Django框架中的QuerySets


Posted in Python onApril 20, 2015

 Django的QuerySets酷毙了!

在本文中我将解释一下QuerySets是什么,它是如何工作的(如果你对它已经熟悉了,你可以直接跳到第二部分),我认为如果可以的话你应该总是返回QuerySets对象,下面让我来谈谈如何做。
QuerySets很酷

QuerySet,本质上是一个给定的模型的对象列表。我说“列表”而不是“组”或更正式的“集合”因为它是有序的。事实上,你可能已经熟悉如何获得QuerySets,因为这就是你调用variousBook.objects.XXX()方法后得到的对象。例如,考虑下面的语句:
 

Book.objects.all()

all()返回的就是Book实例的一个QuerySet,它正好包括allBookinstances,下面的其他调用你可能已经知道:
 

# Return all books published since 1990
Book.objects.filter(year_published__gt=1990)
 
# Return all books *not* written by Richard Dawkins
Book.objects.exclude(author='Richard Dawkins')
 
# Return all books, ordered by author name, then
# chronologically, with the newer ones first.
Book.objects.order_by('author', '-year_published')

关于 QuerySet s最酷的是,由于这些函数操作、返回的都是一个QuerySet,你可以把他们链起来:
 

# Return all book published after 1990, except for
# ones written by Richard Dawkins. Order them by
# author name, then chronologically, with the newer
# ones first.
Book.objects.filter(year_published__gt=1990) \
      .exclude(author='Richard Dawkins') \
      .order_by('author', '-year_published')

而且这并不是全部的,它更快!

    在内部,一个QuerySet可以被构造、过滤、切片及像普通变量那样在没有实际数据库查询的情况下随便传递,在评估处理完QuerySet前不产生数据库活动。

所有我们确认了QuerySets很酷,不是么?

尽可能的返回QuerySets

我最近曾在一个Django应用中用一个模型来表示树(数据结构,不是圣诞装饰)。这意味着每一个实例在树上都有一个指向它父节点的链接。它看起来像这样:
 

class Node(models.Model):
  parent = models.ForeignKey(to='self', null=True, blank=True)
  value = models.IntegerField()
   
  def __unicode__(self):
    return 'Node #{}'.format(self.id)
   
  def get_ancestors(self):
    if self.parent is None:
      return []
    return [self.parent] + self.parent.get_ancestors()

这工作的相当好。麻烦的是,我不得不添加另一种方法,get_larger_ancestors,它应该返回所有值大于当前节点的的父节点。这是我能实现这个:
 

def get_larger_ancestors(self):
    ancestors = self.get_ancestors()
    return [node for node in ancestors if node.value > self.value]

问题是,我基本上会在名单上审查两次——Django一次,我自己一次。这让我考虑到-如果get_ancestors返回QuerySet而不是列表会怎样呢?我可以这样做:
 

def get_larger_ancestors(self):
    return self.get_ancestors().filter(value__gt=self.value)

很简单,这里更重要的是我没有遍历对象。我可以对get_larger_ancestors的返回使用任何我想使用的过滤器,而且感到安全——我不会得到一个未知大小的对象列表。这样的主要优势是我一直使用相同的查询接口。当用户得到了一大堆的对象,我们不知道他想怎样对它们进行切片分块。而返回QuerySet对象时我保证用户知道如何处理它。

但如何实现get_ancestorsto返回一个QuerySet呢?这是一个小技巧。用一条简单的查询收集我们需要的数据是不可能的,使用任何预定数量的查询也是不可能的。我们要找的法则是动态的,选择的实现看起来很像它现在的样子,下面就是选择,一个更好的实现:
 

class Node(models.Model):
  parent = models.ForeignKey(to='self', null=True, blank=True)
  value = models.IntegerField()
   
  def __unicode__(self):
    return 'Node #{}'.format(self.id)
   
  def get_ancestors(self):
    if self.parent is None:
      return Node.objects.none()
    return Node.objects.filter(pk=self.parent.pk) | self.parent.get_ancestors()
   
  def get_larger_ancestors(self):
    return self.get_ancestors().filter(value__gt=self.value)

稍停一会,沉淀一下,马上说出细节。

我想说的是,不论什么时候你返回一系列对象——你应该总是返回一个QuerySet替代。这样做将允许用户使用一种简单、熟悉、具备更好性能的方法自由过滤、剪接和排序结果。

(从一个侧面说get_ancestors查询了数据库,因为我使用了递归的self.parent。这里有一个额外的数据库执行——当实际检测结果时执行了这个函数,未来又执行了另外一次。当我们在数据库查询上使用更多的过滤器或进行高耗内存的操作时我们得到了性能的提升。这里的例子

常见的QuerySet操作

所以,执行简单查询时返回一个QuerySet很简单。当我们想实现复杂一点的东西,我们需要执行相关操作(也包括一些助手函数)。下面是些小窍门(作为练习,试着理解我get_larger_ancestors的实现)。

  •     联合 - QuerySet的联合运算符是|,处理复制时管道“symbol.qs1 | qs2”返回所有来自qs1和qs2项目的QuerySet(都在QuerySet的项目将只在结果中出现一次)。
  •     交集 - 交集没有特殊的操作,因为你已经知道怎么去做。 像filter等链接函数在原始的QuerySet和新过滤器之前起了交集的作用。
  •     差分 - 差分(数学上写为qs1 \ qs2)代表所有在qs1而不在qs2中的项目。请注意,此操作是不对称的(相对于以前的操作)。Python中恐怕没有内置的方式,但你可以这样做:qs1.exclude(pk__in=qs2)
  •     从空开始 - 开起来没有用处但实际并非如此,正如上面例子所展示的。很多时候,当我们动态建立一个QuerySet联合时,我们需要从一个空列表开始,这是获取它的方法:MyModel.objects.none().
Python 相关文章推荐
深入理解python try异常处理机制
Jun 01 Python
Python使用Turtle模块绘制五星红旗代码示例
Dec 11 Python
pandas对指定列进行填充的方法
Apr 11 Python
使用python3调用wxpy模块监控linux日志并定时发送消息给群组或好友
Jun 05 Python
Python CVXOPT模块安装及使用解析
Aug 01 Python
python 动态调用函数实例解析
Oct 21 Python
python list数据等间隔抽取并新建list存储的例子
Nov 27 Python
python不使用for计算两组、多个矩形两两间的iou方式
Jan 18 Python
为什么是 Python -m
Jun 19 Python
python之语音识别speech模块
Sep 09 Python
Python爬虫之Selenium中frame/iframe表单嵌套页面
Dec 04 Python
python调试工具Birdseye的使用教程
May 25 Python
使用Python的Django框架实现事务交易管理的教程
Apr 20 #Python
简化Python的Django框架代码的一些示例
Apr 20 #Python
在Python的Django框架上部署ORM库的教程
Apr 20 #Python
在Heroku云平台上部署Python的Django框架的教程
Apr 20 #Python
从Python程序中访问Java类的简单示例
Apr 20 #Python
把项目从Python2.x移植到Python3.x的经验总结
Apr 20 #Python
python使用7z解压apk包的方法
Apr 18 #Python
You might like
destoon找回管理员密码的方法
2014/06/21 PHP
ThinkPHP中order()使用方法详解
2016/04/19 PHP
ThinkPHP实现附件上传功能
2017/04/27 PHP
JScript的条件编译
2007/05/29 Javascript
基于jquery的横向滚动条(滑动条)
2011/02/24 Javascript
js无刷新操作table的行和列
2014/03/27 Javascript
使用JavaScript判断图片是否加载完成的三种实现方式
2014/05/04 Javascript
原生javascript实现的分页插件pagenav
2014/08/28 Javascript
45个JavaScript编程注意事项、技巧大全
2015/02/11 Javascript
JS获得多个同name 的input输入框的值的实现方法
2017/01/09 Javascript
微信小程序 label 组件详解及简单实例
2017/01/10 Javascript
canvas实现粒子时钟效果
2017/02/06 Javascript
详解从新建vue项目到引入组件Element的方法
2017/08/29 Javascript
[js高手之路]单例模式实现模态框的示例
2017/09/01 Javascript
javascript基于定时器实现进度条功能实例
2017/10/13 Javascript
angular2模块和共享模块详解
2018/04/08 Javascript
Swiper 4.x 使用方法(移动端网站的内容触摸滑动)
2018/05/17 Javascript
使用wxapp-img-loader自定义组件实现微信小程序图片预加载功能
2018/10/18 Javascript
js 将线性数据转为树形的示例代码
2019/05/28 Javascript
关于微信小程序map组件z-index的层级问题分析
2019/07/09 Javascript
用JavaScript实现贪吃蛇游戏
2020/10/23 Javascript
Python遍历zip文件输出名称时出现乱码问题的解决方法
2015/04/08 Python
Python中read()、readline()和readlines()三者间的区别和用法
2017/07/30 Python
Python实现字符串匹配算法代码示例
2017/12/05 Python
Python数据分析之双色球中蓝红球分析统计示例
2018/02/03 Python
一行Python代码过滤标点符号等特殊字符
2019/08/12 Python
解决python3 requests headers参数不能有中文的问题
2019/08/21 Python
python函数map()和partial()的知识点总结
2020/05/26 Python
python实现图片,视频人脸识别(opencv版)
2020/11/18 Python
用ldap作为django后端用户登录验证的实现
2020/12/07 Python
data:image data url 文件转为Blob上传后端的方法
2019/07/16 HTML / CSS
CheapTickets泰国:廉价航班,查看促销价格并预订机票
2019/12/28 全球购物
师范大学音乐表演专业求职信
2013/10/23 职场文书
优秀应届毕业生推荐信
2014/02/18 职场文书
法定代表人身份证明书(含说明)
2014/10/02 职场文书
Android RecyclerView实现九宫格效果
2022/06/28 Java/Android