模板语言是越傻越好

For years I’ve been absolutely certain that I really prefer stupid template languages any time I’m generating HTML. The less the template language can do the better. Since I spend most of my time coding in Python you might assume this applies just to Python, but I think it also applies to anything where you have the power to readily mix HTML generation and code.

The biggest annoyance I have with smart template languages (Mako, Genshi, Jinja2, PHP, PerlColdFusion, etc) is that you have the capability to mix core business logic with your end views, hence violating the rules of Model-View-Controller architecture. While the web can be hard to match to MVC, in general you aren’t supposed to do that sort of thing. I’ve made the mistake of putting core logic in the wrong places in the past, but I’m proud to say I’ve gotten good at avoiding that particular mistake.

I don’t work in a vacuum.

I often work on projects crafted by others, some who decided for arcane/brilliant/idiotic reasons to mix the kernel of their applications in template function/macros. This is only possible in Smart Template Languages! If they were using a Stupid Template Language they would have been forced put their kernel code in a Python file where it applies, not in a template that was supposed to just render HTML or XML or plain text.

What it comes down to is that Smart Template Languages designers assume that developers are smart enough to avoid making this mistake. Stupid Template Languages designers assume that developers generally lack the discipline to avoid creating horrific atrocities that because of unnecessary complexity have a bus factor of 1.

So what is a Smart Template Language?

In my own vernacular, template languages that let you write functions/macros are what I call a Smart Template Language. Some of them are brilliantly executed, the example of Jinja2 comes to mind, but invariably I suffer through abuse of its Macro control structure as implemented by others.

Misery Cubed a.k.a. Genius Template Languages

Next comes Genius Template Languages, which take things a step further. These template languages allow you to not only define functions/macros, but also let you embed unrestricted Python (or Java or Ruby or whatever) in the template. This ‘feature’ lets you code your entire application in the templates!  In the Python world what comes to mind is Mako and Genshi, but I’m sure there are many other tools with this ‘capability’.

I like Stupid Template Languages!

Stupid Template Languages don’t let you define functions/macros. They don’t let you embed Python code. They barely let you define variables and often have simplistic control architectures.

For Django efforts, which is about 70% of my work, I like the Django Template Language (DTL). Since it is used by a huge community, there are a ton of useful apps which have it as a dependency. Switching away from it would mean cutting myself off from a large ecosphere of tools I can use to not reinvent the wheel.

Back in my Zope/Plone days I really, really enjoyed the Template Attribute Language (TAL) because it was stupid too. If I needed an XML generation template language and could import it easily I might consider using it again, or perhaps Chameleon, which is a new, improved version . The downside is that they come paired with another tool paired with it, METAL, which gave it macros. My own experience with METAL is that it was all too easy to do what we developers do with Smart Template Languages.

But DTL and TAL are slow!

So what?

If you want to boost your performance, first try caching. There are a ton of tools you can use, with Varnish being one I keep seeing in action. Read the docs on your favorite web framework’s caching engine and apply what you learn. And Djangonauts should read up on Mike Malone as much as possible.

If after all that the site still delivers slow content and it appears to be a template language issue, then identify the bottleneck content and consider alternatives for that one portion. My favorite response is a bit of AJAX. Use your framework to render the content as JSON and have JavaScript parse it into legible content, a task which JQuery makes trivial.

From: http://pydanny.blogspot.com/2010/12/stupid-template-languages.html

在线谷歌地图编辑器

前几天,抽时间把原来的一个Django项目移植到了Google App Engine上,在Google App Engine上面使用Django还是挺方便的,主要有两个主要项目用于帮助更好的使用Django。以下是这两个项目:

Google App Engine Helper for Django

app-engine-patch

这两个项目的出发点不一样,但我没有使用过app-engine-patch,所以对个项目的评价是不搞谱的。Google App Engine Helper for Django引入了一个经过修改了Model基类,以使之能在Google App Engine上使用Django的model功能。

总的来说Google App Engine Helper for Django这个项目能帮助你尽快的将Django项目移植到Google App Engine上去。

在线谷歌地图编辑器:http://gmap2s.appspot.com

用于帮助你生成在线地图的代码。

用PHP实现Django的模板系统

这几天一直没有在做lifefix的开发工作,因为突然想用PHP实现一个Django的模版系统。如果不去试一试这个想法的话,估计我的开发工作也做不得不安宁。

开始分析django模板的源代码,一边用PHP来实现,现在基本上理解了其原理,现正在一步一步的实现。啊,期待一个完整的实现。

更新:现在已经移植了大部分功能,但自定义tag方面还没有完成,然后代码还有优化的余地。下周最好整理一下代码,然后发布出来。

移植工作已经基本上完成了,还有些filter没移植,但是支持PHP内部函数,发布在http://bitbucket.org/gonefish/shine/这里。

现在在准备文档。

signals的存放位置

根据官方文档signals一般放在app中的management.py文件中,但我在使用svn中的版本时候,发现通过view的时候尽然不会调用signals,但在命令行和测试中都可以。

如果你也有这样的问题,那么把management.py中的代码放到app的__init__.py中,这样就没有问题。

最新的开发版本中的session修改

原来,想实现一个记住我的功能时,需要修改django的代码,因为session中间件对处理记录sessonid的cookie过期时间是写死的。在最新的开发版中,对session对象添加了几个有用的方法,set_expiry(),这个方法根据接受的参数的不同的有不同的效果。

在一般情况下,站点默认使用cookie关闭浏览即取消,当使用记住我的时候,cookie则在某个时间内取消。

首先,需要把SESSION_EXPIRE_AT_BROWSER_CLOSE = True

然后,写个装饰器:

def remember_me(view_func):
    def _dec(request, *args, **kwargs):
        if request.POST.get(‘rme’) == ‘1’:
            request.session.set_expiry(request.session.get_expiry_age())
        return view_func(request, *args, **kwargs)
    return _dec

其中rme是记住我的表单项,对django.contrib.auth.views.login使用这个装饰器,呵呵这样就在不用修改login代码的情况下添加了记住我的功能。

在Django中直接将用户login

在设计站点的时候,也许会有这种情况:当用户注册后,直接帮当前用户登陆,这时可以使用from django.contrib.auth import login的方法来完成用户的状态的改变,但这里有点问题,就是在设置session的时候,会设置一个BACKEND_SESSION_KEY这个东西,如果你使用默认的login view的时候使用from django.contrib.auth import authenticate来对User对象设置一个值。如果不使用authenticate的话,那只有手工自己设置了:

new_user.backend = “%s.%s” % (‘django.contrib.auth.backends’, ‘ModelBackend’)

这样就OK了

Django中与Model有关的三个特性

使用Django也差不多有一段时间了,而且也在项目中使用了,刚开始时代码写的很烂,现在看起来非常不爽。我想说一下关于model部分的问题,希望能给初学者一些建议吧。

这里有三个关键部分:Managermodel方法Signals。Django是一种非常快速的开发框架,有时候文档还没看完,就能写好多东西,但如果看了文档,那么可以以更少的代码完成相同的功能。

根据Manager的文档说明,Manager用于操作表级别的数据,可以用来对表中的多个项进行操作,在Manager中可以做任何事情,所以再看了Signals后,感觉功能有重叠,等下我会说明为什么重叠。当默认Manager提示的功能不能满足你的需要的时候,那么你就扩展它吧,而且model还可以有多个Manager。

Model方法是行级别的方法,用于对行进行操作,比如:当ModelA是ModelB的外键时,那么可以通过ModelA的Model方法来创建ModelB。Model中的方法最封装成不需要传参数的方法,这样就可以直接在模版中调用了,这样大大减少了代码量。当然Manager也可以这样做,通过for标记遍历Querysets,然后直接调用Model中的方法。

Signals这个东西是非常有用的一个机制,但这个东西还没有正式的文档。所以知名度不是很高,但还是有一个Wiki页面说明了这个东西使用。其实Signals在Django中有大量的应用,比如:在初次syncdb的使用,会让你创建超级管理员,在这里就有一个Signal,post_syncdb,当系统执行syncdb的时候,会调用已监听的方法,通过这个方法实现创建超级管理员向导。系统自带许多Signal,其中有个post_save,这个Signal用于监听Model对象保存后,执行的操作,比如:在现在中都会添加一个UserProfile来扩展User Model,但自带的添加用户方法,并不会添加UserProfile,所以可以使用post_save来监听User Model,当创建User之后,再创建UserProfile,这样不必修改Django源代码就可以使用。当然你也可以使用Manager来完成这个操作,这就是我说的重复部分,怎么用那就看自己的了。

使用django第三方的库小提示

在使用Django的开发过程中,可能会经常会用其它非官方的组件。如果你使用使用它们,一般情况下必须把它们放在项目的根目录下,或者是python的其它环境变量下,如果想把它们整合进项目,但又不想放在项目的根目录,那么可以在settins中加入一个环境变量,比如:

DJANGO_THIDR_COMPONENTS_PATH = ‘django-third’
sys.path.append(os.path.join(os.path.dirname(__file__), DJANGO_THIDR_COMPONENTS_PATH))

生成Django文档

由于这几天被逼着写一些文档,顺便找了下跟Django有关的文档。后面发现在一直没注意的一个地方,就是在admin里面有个Documentation的链接,点击进入后如果已经安装了docutils库,则显示一些文档,包括:Tags、Filters、Models、Views、Bookmarklets。其中在Models中,会显示定义的Field和类方法,类和类的方法都可以使用__doc__来写注释。而Field则有一个help_text参数,用于描述Field。Views的部分功能好像有点问题。

一切都非常好。