Read Sean

Read me, read Sean.
posts - 508, comments - 655, trackbacks - 9, articles - 4

[Pylons] Routes和controller,一个简单的例子

Posted on 2009-01-26 16:21 laogao 阅读(3952) 评论(0)  编辑  收藏 所属分类: On Python

在开始之前,说点提外话,随着对Pylons了解的深入,你可能时不时需要看看相关组件/软件包是否有更新出来,方法也不复杂,通过"easy_install -U [组件名]"即可,在学习或者是开发过程中,最好是保持环境相对较新,直到出现相对大的release或者即将进入产品部署阶段。

继续介绍Pylons组件,先看个例子。首先用"paster controller hello"增加一个controller,路径中会增加出以下两个文件:
controllers/hello.py
tests/functional/test_hello.py

分别对应新增的controller类HelloController和功能测试类TestHelloController,它们分别继承自WSGIController->BaseController和TestCase->TestController。

我们主要看hello.py,默认内容如下:
 1 import logging
 2 
 3 from pylons import request, response, session, tmpl_context as c
 4 from pylons.controllers.util import abort, redirect_to
 5 
 6 from newapp.lib.base import BaseController, render
 7 #from newapp import model
 8 
 9 log = logging.getLogger(__name__)
10 
11 class HelloController(BaseController):
12 
13     def index(self):
14         # Return a rendered template
15         #   return render('/template.mako')
16         # or, Return a response
17         return 'Hello World'

如果你的服务器没有Ctrl-C停掉,那么这个时候你已经可以通过
http://127.0.0.1:5000/hello/index
看到该controller的处理结果了(Hello World)。

简单改造一下17行:
        from pylons import config
        
return '<br/>'.join(config.keys())

我们就可以在返回页面上显示出所有可以通过pylons.config访问到的参数列表。出了返回文本,也可以通过render()方法交给页面模板引擎生成页面,也可以通过redirect_to()跳转到其他URL。

Pylons是如何找到该请求应该由HelloController的index方法来处理的呢?这背后发生了什么?答案就是Routes。

Routes的作者是Ben Bangert,是Pylons框架三个主要作者/维护者之一,早期的版本主要是仿照Ruby on Rails的routes.rb开发的,有RoR经验的朋友可能一眼就能发现它们之间的相似之处。目前Routes的最新版是1.10.2。

Pylons应用中,routing的配置在config/routing.py,默认生成的内容如下:
 1 """Routes configuration
 2 
 3 The more specific and detailed routes should be defined first so they
 4 may take precedent over the more generic routes. For more information
 5 refer to the routes manual at http://routes.groovie.org/docs/
 6 """
 7 from pylons import config
 8 from routes import Mapper
 9 
10 def make_map():
11     """Create, configure and return the routes Mapper"""
12     map = Mapper(directory=config['pylons.paths']['controllers'],
13                  always_scan=config['debug'])
14     map.minimization = False
15     
16     # The ErrorController route (handles 404/500 error pages); it should
17     # likely stay at the top, ensuring it can always be resolved
18     map.connect('/error/{action}', controller='error')
19     map.connect('/error/{action}/{id}', controller='error')
20 
21     # CUSTOM ROUTES HERE
22 
23     map.connect('/{controller}/{action}')
24     map.connect('/{controller}/{action}/{id}')
25 
26     return map

在这个配置中,对我们刚才的实例起到决定性作用的是第23行,我们的输入URL为"http://127.0.0.1:5000/hello/index",其中"/hello/index"通过"/{controller}/{action}"这个表达式match出controller为hello而action为index的解析结果,从而在controllers目录找到hello.py,和其中HelloController的index方法,进行调用。

map.connect()在上面代码中体现出两种用法:
map.connect('pattern', key=value) - 指定默认的controller、action、id等
map.connect('pattern') - 直接指定pattern

pattern字符串允许通配符,通常在最后一个元素上,比如'/{controller}/{action}/{*url}',将后面的整个URL片段交给前面指定的controller/action处理。除此以外,map.connect()还支持

1- "路径别名",如:
map.connect('name', 'pattern', [_static=True])
如果_static设为"True",表示为"静态命名路径"。
2- 额外的匹配条件,如:
map.connect('/{controller}/{action}/{id}', requirements={'year': '\d+',})
map.connect('/{controller}/{action}/{id}', conditions=dict(method=['GET','POST']))

所有的route优先级为从上到下。Routes除了提供解析进来的URL的逻辑,在我们的controller和template代码中,我们还可以方便的通过WebHelpers的url_for()方法计算相应的URL。

Routes 1.x中的有一些仿routes.rb功能将会在2.0中被去掉,包括Route Minimization、Route Memory、Implicit Defaults等。如果有兴趣的话,可以参考一下官方文档,这里就不一一介绍了。为什么要去掉?当然主要的动机还是减少歧义,避免一些不必要的混淆。至于深层次的原因么,可以参考Tim Peters《The Zen of Python》中的一句经典的Python哲学:Explicit is better than implicit。什么?没有听说过?打开python命令行,输入"import this"后回车,慢慢体会其中的道理吧。:)



只有注册用户登录后才能发表评论。


网站导航: