建立一个更高级别的查询 API:正确使用Django ORM 的方式
2014-11-23 10:56
741 查看
http://www.oschina.net/translate/higher-level-query-api-django-orm
英文原文:Using Django's low-level ORM query methods directly in a view
is (usually) an anti-pattern.
摘要 在这篇文章里,我将以反模式的角度来直接讨论Django的低级ORM查询方法的使用。作为一种替代方式,我们需要在包含业务逻辑的模型层建立与特定领域相关的查询API,这些在Django中做起来不是非常容易,但通过深入地了解ORM的内容原理,我将告诉你一些简捷的方式来达到这个目的。 概览 当编写Django应用程序时,我们已经习惯通过添加方法到模型里以此达到封装业务逻辑并隐藏实现细节。这种方法看起来是非常的自然,而且实际上它也用在Django的内建应用中。 ? ? | 晴风晓月 翻译于 2年前 2人顶 顶 翻译的不错哦! |
我们正在使用Django,建立一个特定领域的顶部通用接口,低等级的ORM工具。在此基础上,增加抽象等级,减少交互代码。这样做的好处是使代码更具可读性、重用性和健壮性。 我们已经在单独的例子中这样做了,下面将会把它用在获取数据库信息的例子中。 为了描述这个方法,我们使用了一个简单的app(todo list)来说明。 注意:这是一个例子。因为很难用少量的代码展示一个真实的例子。不要过多的关心todo list继承他自己,而要把重点放在如何让这个方法运行。 下面就是models.py文件: ? | Naixjs 翻译于 2年前 1人顶 顶 翻译的不错哦! |
想像一下,我们将要传递这些数据,建立一个view,来为当前用户展示不完整的,高优先级的 Todos。这里是代码: ? 为什么这样写不好呢? 首先,代码冗长。七行代码才能完成,正式的项目中,将会更加复杂。 其次,泄露实现细节。比如代码中的is_done是BooleanField,如果改变了他的类型,代码就不能用了。 然后就是,意图不清晰,很难理解。 最后,使用中会有重复。例:你需要写一行命令,通过cron,每周发送给所有用户一个todo list,这时候你就需要复制-粘贴着七行代码。这不符合DRY(do not repeat yourself) | Naixjs 翻译于 2年前 1人顶 顶 翻译的不错哦! |
让我们大胆的猜测一下:直接使用低等级的ORM代码是反模式的。 如何改进呢? 使用 Managers 和 QuerySets 首先,让我们先了解一下概念。 Django 有两个关系密切的与表级别操作相关的构图:managers 和 querysets manager(django.db.models.manager.Manager的一个实例)被描述成 “通过查询数据库提供给Django的插件”。Manager是表级别功能的通往ORM大门。每一个model都有一个默认的manager,叫做objects。 Quesyset (django.db.models.query.QuerySet) 是“数据库中objects的集合”。本质上是一个SELECT查询,也可以使用过滤,排序等(filtered,ordered),来限制或者修改查询到的数据。用来 创建或操纵django.db.models.sql.query.Query实例,然后通过数据库后端在真正的SQL中查询。 啊?你还不明白? 随着你慢慢深入的了解ORM,你就会明白Manager和QuerySet之间的区别了。 | Naixjs 翻译于 2年前 1人顶 顶 翻译的不错哦! |
人们会被所熟知的Manager接口搞糊涂,因为他并不是看上去那样。 Manager接口就是个谎言。 QuerySet方法是可链接的。每一次调用QuerySet的方法(如:filter)都会返回一个复制的queryset等待下一次的调用。这也是Django ORM 流畅之美的一部分。 但是当Model.objects 是一个 Manager时,就出现问题了。我们需要调用objects作为开始,然后链接到结果的QuerySet上去。 那么Django又是如何解决呢? 接口的谎言由此暴露,所有的QuerySet 方法基于Manager。在这个方法中,通过self.get_query_set()的代理,重新创建一个QuerySet。 ? | Naixjs 翻译于 2年前 1人顶 顶 翻译的不错哦! |
让我们立刻回到todo list ,解决query接口的问题。Django推荐的方法是自定义Manager子类,并加在models中。 你也可以在model中增加多个managers,或者重新定义objects,也可以维持单个的manager,增加自定义方法。 下面让我们实验一下这几种方法: 方法1:多managers ? ? | Naixjs 翻译于 2年前 1人顶 顶 翻译的不错哦! |
第一,这种实现方式比较啰嗦。你要为每一个query自定义功能定义一个class。 第二,这将会弄乱你的命名空间。Django开发者吧Model.objects看做表的入口。这样做会破坏命名规则。 第三,不可链接的。这样做不能将managers组合在一起,获得不完整,高优先级的todos,还是回到低等级的ORM代码:Todo.incomplete.filter(priority=1) 或Todo.high_priority.filter(is_done=False) 综上,使用多managers的方法,不是最优选择。 | Naixjs 翻译于 2年前 1人顶 顶 翻译的不错哦! |
方法2: Manager 方法 现在,我们试下其他Django允许的方法:在单个自定义Manager中的多个方法 ? ? 不过这还不够全面。 Todo.objects.incomplete() 返回一个普通查询,但我们无法使用Todo.objects.incomplete().high_priority() 。我们卡在 Todo.objects.incomplete().filter(is_done=False),没有使用。 | fkkeee 翻译于 2年前 1人顶 顶 翻译的不错哦! |
方法3:自定义QuerySet 现在我们已进入Django尚未开放的领域,Django文档中找不到这些内容。。。 ? ? ? | fkkeee 翻译于 2年前 1人顶 顶 翻译的不错哦! |
方法3a:复制Django,代理做所有事 现在我们让以上”假冒Manager API“讨论变得有用:我们知道如何解决这个问题。我们简单地在Manager中重新定义所有QuerySet方法,然后代理它们返回我们自定义QuerySet: ? ? | fkkeee 翻译于 2年前 2人顶 顶 翻译的不错哦! |
方法3b: django-model-utils Python 是一种动态语言。 我们就一定能避免所有模块?一个名叫Django-model-utils的第三方应用带来的一点小忙,就会有点不受控制了。先运行 pip install django-model-utils ,然后…… ? PassThroughManager 是由__getattr__ 实现的,它能阻止访问到django定义的“不存在的方法”,并且自动代理它们到QuerySet。这里需要小心一点,检查确认我们没有在一些特性中没有无限递归(这是我为什么推荐使用django-model-utils所提供的用不断尝试测试的方法,而不是自己手工重复写)。 做这些有什么帮助? 记得上面早些定义的视图代码么? ? ? Django能帮忙么? | fkkeee 翻译于 2年前 3人顶 顶 翻译的不错哦! |
让这整个事情更容易的方法,已经在django开发邮件列表中讨论过,并且得到一个相关票据(译注:associated ticket叫啥名更好?)。Zachary Voase则建议如下: ? 我个人并不完全赞同使用基于装饰方法。它略过了详细的信息,感觉有点“嘻哈”。我感觉好的方法,增加一个QuerSet子类(而不是Manager子类)是更好,更简单的途径。 或者我们更进一步思考。退回到在争议中重新审视Django的API设计决定时,也许我们能得到真实更深的改进。能不再争吵Managers和QuerySet的区别吗(至少澄清一下)? | fkkeee 翻译于 2年前 2人顶 顶 翻译的不错哦! |
我很确信,不管以前是否曾经有过这么大的重构工作,这个功能必然要在Django 2.0 甚至更后的版本中。 因此,简单概括一下: 在视图和其他高级应用中使用源生的ORM查询代码不是很好的主意。而是用django-model-utils中的PassThroughManager将我们新加的自定义QuerySet API加进你的模型中,这能给你以下好处: 啰嗦代码少,并且更健壮。 增加DRY,增强抽象级别。 将所属的业务逻辑推送至对应的域模型层。 感谢阅读! | fkkeee 翻译于 2年前 2人顶 顶 翻译的不错哦! |
相关文章推荐
- 建立一个更高级别的查询 API:正确使用Django ORM 的方式 - 技术翻译 - 开源中国 OSChina.NET
- 建立一个更高级别的查询 API:正确使用Django ORM 的方式(转)
- 建立一个更高级别的查询 API:正确使用Django ORM 的方式
- 【转】建立一个更高级别的查询 API:正确使用Django ORM 的方式
- 利用Python的Django框架中的ORM建立查询API
- django使用orm方式查询mogodb的某段时间的值
- 使用api方式查询数据库和sqlite3工具
- 使用Django开发一个图书管理系统 03----迈出第一步,建立项目
- 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
- 基于Wolf32F031 自由评估板的KEY scan(查询方式)实验硬件原理及软件API使用方法(适用于STM32F030/031)
- Linux内核分析:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
- django:数据库修改工具South的正确使用方式
- 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
- django 的 ORM 单独使用 - vkill'blog - “技术本身没有太多价值,掌握了新的思考方式才是真的收获”
- 使用Python的web.py框架实现类似Django的ORM查询的教程
- 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
- 库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
- Fileatream表示文件流,它能够打开和关闭文件,并对文件进行单字节的读写操作。 StreamReader和StreamWriter以文本方式对流进行读写操作。建立一个文本文件,分别使用上面两种方
- 以正确的方式开始一个 Django 1.6 项目
- 写一个程序用来查询手机号码的归属地(使用的是HttpURLConnection的get提交方式)