Django 补丁接受
这是本人第二枚遭到接受的补丁,上次是2010年8月。
补丁内容是把所有异常的提出方式改为 raise Exception(msg) 的形式,为将来的 Python 3 移植作准备。
提交到源里的是Django的创建者之一 Adrian Holovaty
令人震惊的是,这个补丁从提交到进入代码库只花了2个小时!从我产生该想法到进入源玛也不到3个小时。Django 社区的效率较之从前真是高了许多呀。
Django短讯三则
秋天是收获的时节。
Django 社区经过一个夏天的努力也获得了不少成果,本文着重介绍最近(本文发表于西六区2010年9月9日夜)DaNmarner了解到的几条 Django 新闻。
按照发生的时间顺序:
- GSoC 2010结束.
GSoC 是 Google 每年掏钱让在校大学生帮开源项目搞开发的大好事。今年 Django 一共获得了三个名额,由三个参与学生的姓名及工作内容分别是:
- Arthur Koziel,优化 App 加载过程的代码。
- Paul McMillan, 重写和更新 Django 的测试代码。
- Alex Gaynor,调整和优化 ORM 结构,为支持 NoSQL 数据库做好准备。 其中 DaNmarner 最关注的是不久前提过的 Query Refactor工程。Alex Gaynor 在 2009 年的 GSoC 里面为 Django 的 ORM 带来了多数据库支持。今年则是放弃了原本优化 Template 系统的计划,重操旧业搞起了 ORM 。他的工作有两个目标:正式的目标是优化 ORM 的结构,让它的混乱值进一步降低。非正式的目标是为 Django 将来对 NoSQL 数据库的支持铺路。后者是我关注他的工作的真正原因,为了保证效果,Alex 在调整 Django ORM 代码的同时还写了一个 MongoDB 的 backend 作为演示。更让人(或者只有 DaNmarner 一人?)兴奋不以的是他还针对这个 backend 加入了一个 ListField。由于 MongoDB 和 Google App Engine 的 BigTable 数据库有很多相似之处,Django 对后者的原生支持也是指日可待的了。更多信息见 Alex 在 Django Dev 上对他的 GSoC 工作进行的最后通讯以及总结。
- Django 1.2.2 发行 此版本修正了几个有关 CSRF 的安全问题,强烈建议已经在使用1.2的各位同学升级。DaNmarner 对这个版本也有所贡献。更多有关Django 1.2.2 的内容参见发行注记。 另外,今天下午6点多在 Django Dev 上又传出消息说 Django 这两天可能又要紧急发布一个 1.2.3 版本。因为有几个重大问题还没有解决。
- django-nonrel 即将支持 Join 数据库操作 本消息来自 django-nonrel 创建者的一条推。 如果你对 NoSQL 稍有了解,这个信息可能会有点难以消化。是的,Join 本身是 SQL 数据库里独有的操作,有没有 Join 是 NoSQL 和 SQL 数据库在查询数据操作上的决定性差异。既然是 django-nonrel,为什么又要去支持 Join 呢?到底是怎么个支持法呢?原来 Django 的 ORM 中大量的 API 是跟 Join 操作有关的。比如基本的 ManyToMany 关系,ForeignKey 的反向查询等等。现存的大量 Django 应用对这些操作多少都有一些依赖。换言之,这些应用在 NoSQL 类型的数据库作为 backends 的时候根本没法运行!这也是 Django 在 GAE 上不能运行其自带的 admin 的根本原因。django-nonrel 创建的目的就是通过维护一个 fork 的 Django 版本以及几个周边应用,让这些应用在不修改代码的前提下在 NoSQL 数据库上运行起来。当前的 django-nonrel 已经能让你在 GAE 上使用 admin 了。他们的解决方案是在内存里模拟传统数据库中对两个表的 Join 操作,这是一个不是办法的实现,使用不慎可能会引爆内存。但是现在有了 Join 操作,大批的主流 Django 应用都能顺利跑在 GAE 上了。
其实这几条短讯都可以分开单独写成博文的,懒得折腾了所以写在一起吧。
其实 Django 社区真正的大丰收是今天刚刚正式闭幕的 DjangoCon US,相信现场演讲的 Slides 和 Videos 应该很快就能在线观看了。有些演讲者已经把 Slides 上传了,比如这里和这里(抗议一下,为啥大家都在用 Apple Keynote,上传 PDF很麻烦吗?)。会议结束后的 Sprint 今天正式开始,这种 Sprint 通常都能折腾出不少好东东来,我们拭目以待。
更新:更多的 DjangCon US 演讲 Slides 可以在 djangocon.us 官网 上找到。Video 可以在 blip.tv 找到。
Django 官方接受本人补丁一枚
很久没 follow Django 的开发进展了, 今天 pull 了一下 Github 上的半官方代码树,发现几个月前我在 http://djangoproject.org 的 Trac 上提交的 Ticket #13615 以及补丁已经于2010年8月3日在代码树的 releases/1.2.x 分支得到修正,详见官方svn仓库的 ChangeSet 13512。Checkin 我的补丁的核心也是更久以前在 Trac 上指导我改进补丁内容的 Russell Keith-Magee。
说来惭愧,这第一枚补丁其实没有涉及到 Django 的功能代码,而是仅仅修改了一处 Exception 的提示语。不过作出这个修改也确实是因为当初一第次碰到这个提示的时候被困惑了一下下。具体在 Trac 上都写了,算是有点轻微的实际意义吧。
虽然就是这么简单的一点事情,总的来说还是很有价值的:
-
感受到大项目里想当然的行事很不妥。当时搞清楚了那个 Exeption 就随手改了 Django 代码,grep 了一下发现修改的部分没有测试代码,就想当然的 diff 了一个补丁传到了 Trac 上。 结果几天后被指出这个补丁会导致几个 Test 不能通过,所以不得不再 grep 发现原来有几个测试其他代码的 test 用到了这个 exception 的错误提示。于是改掉,再提交第二个 patch 才得以接受。
-
初步体验了一下参与开源项目开发的流程。
-
把 DaNmarner 这个 ID 留在了 Django 的历史里,这只是个开始,只是个开始,是个开始,个开始,开始,始……
没用的select_template
UPDATE:其实“模板和它子模板的相对位置固定”这个假设是个很不好的主意——假设用户只想在不同的目录定制某一个模板,那该假设要求她把父模板也复制到新目录下面。这和定制的目的是背道而驰的。所以本文发表的有点不成熟。比较合理的解决办法是在 view 里用 select_template 把父模板(以及所有其他需要的模板)编译了,然后以参数的形式传给模板里的 extends 标签。然而这也不是一个完美的解决方案, Sophie 现阶段的模板里用到了 include 标签,这个标签是不接受编译过的模板作为参数的!换句话说,假设我要在模板A里 include 一个 sidebar.html,那我在定制A的时候就不能在 view 里用 select_template 编译原目录的 sidebar.html,然后穿给新目录里的 A.html。现在有一个 workaround,就是把 sidebar.html 在 view 里 render 了,然后把 render 的结果以模板变量的形式代替原来的 include 标签。但这毕竟只是个 workaround。更好的方案或许包括 1 用 templatetag 实现 include 标签的效果。2 Hack Django 的代码,让 include 接受编译过的模板作为参数。目前 Sophie 接受的是方案1,方案2已经作为一个 ticket 提交给 Django 了,有时间再写这个 patch 吧。
Django有个帮助搜索模板的函数叫做 django.template.loader.select_template,它接受一个函数名列表,依次在可能的位置寻找列表中的每一项,一旦找到任何一个,马上停止搜索并返回这个编译过的模板。
什么是可能的位置呢?settings.py里制定的所有位置,以及所有TEMPLATE_LOADER想找的位置。
文档里还提到利用这个函数可以获得很灵活的模板调用,比如可以先尝试寻找针对某个页面的特殊模板名,如果不存在就使用默认模板名之类的。听起来不错吧?
不过今天DaNmarner试用了这个函数才发现原来它一点都不好用!
这么说的原因是,既然用了这个函数,那么模板的位置当然是提前不知道,运行时决定。然而模板一般都包含一个 extends 标签,用来扩展某个基模板。一般基模板和扩展模板的相对位置是固定的,可是它们的绝对位置是运行时决定啊!这要我怎么写这个 extends 标签呢?用一个变量作为参数倒是可以,难道我还要自己再写一套逻辑来搜索基模板,再吧位置加入 context 吗?
太浮躁,太浮躁。不知道是谁写的这个函数,完全不切实际啊。
今天为了解决这个问题,我不得不从 Django 里复制出 select_template 的源码来获取找到的路径。当然为了彻底解决这个问题我也顺便提交了一个 ticket: Ticket #13984
Django终于有了ListField
前不久 @alex_gaynor 在 Django Dev 上汇报他的 GSoC 工作进展的时候提到他即将着手为 Django 写一个 ListField,用来整合即将原生支持的 NoSQL 数据库的 List 数据类型以及 SQL 数据的 Array。今天 DaNmarner 在例行更新了 Django 的源码之后意外发现 Alex 已经把 ListField 部分的代码推送到官方代码仓库了!
当然,这个 ListField 当前只能在 Alex 的 MongDB 数据后端上工作,然而通过代码中的测试我们已经能看到这个 API 的雏形了。今天仓库里暴露的 API 很简洁 ::
Class MyClass(models.Model): # ... tags = ListField(models.CharField(max_length=250)) # ...
这样以后 tags 属性就可以 Python List的形式操作了。
为什么 DaNmarner 这么关注这个 ListField 呢?
其实是和 django-sophie 的开发路线图 有关。Sophie 1.0 的开发计划中包含了对 Tagging 的支持,Tagging 和博客的文章以及页面等等是 Many To Many 的关系。但是 Sophie 的另一特性——可以运行在 GAE 上决定了 Django 的 ManyToMany 类型不能使用,这一点就枪毙了现存的所有 tagging 程序。所以在 GAE 以及 SQL 数据后端同时支持 Tagging 是个令人头痛的问题。
为此我曾经考虑过几个解决方案:
-
牺牲空间来储存 Tag 和 Entry 的关系,比如 entry1 和 tag1 关联了以后就储存一个 EntryRel 实例,它包含一个指向 tag1 的 ref,同时再储存一个 TagRel, 包含指向 entry1 的 ref。这样只靠基本的 ForeignKey 就能双向的找到 Entry 和 Tag 的关联。这是个很天真的方案,因为该算法的空间复杂度太高,数据库空间吃的太凶了!(这分析怎么感觉这么小白)
-
在 django-nonrel 环境(mongoDB,GAE)利用 ListField 写一个 tagging 的替代品,nonrel 环境里用,其他时间用正常原版的 tagging 程序。我认为这是最为可行的。
-
在 nonrel 项目里用 ListField 模拟 SQL 数据的 Join 操作。这个看似一劳永逸的方案和1面临着相同的问题:空间复杂度太科幻,只不过这次吃的不是数据库,而是内存……
-
用 Django 提供的数据类型模拟一个 ListField,这个通过 pickling 之类的办法其实不难实现,但……
但是现在什么办法都是多余的了!
Alex 这个 ListField 将彻底解决 Sophie 的 tagging 难题!
最后顺便补充一下当前主流的 Tagging 应用:
- django-tagging : 老牌的应用,代码里有很多 SQL 语句,现在维护的很少,不过感觉用户还是很多。
- django-taggit :新兴的 tagging 应用,作者又是我们的 @alex_gaynor同学。