This repository has been archived by the owner on Oct 12, 2017. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathRoutesUrlGeneration
152 lines (116 loc) · 4.89 KB
/
RoutesUrlGeneration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
= Routes URL generation =
For those who use Routes and RoutesDispatcher, making use of the url generation capabilities can be a great timesaver.
To make Routes generate a url, call {{{routes.url_for}}} with keyword parameters corresponding to the Routes rules. To account for urls hidden behind mod_proxy or mod_rewrite it's also good to pass that url through {{{cherrypy.url}}}.
Say we have this setup:
{{{
#!python
# -*- encoding: UTF-8 -*-
import os
import cherrypy
class MainController:
def index(self):
return "This is the main page"
class BlogController:
def index(self, entry_id):
return "This is blog entry no. %d" % int(entry_id)
def edit(self, entry_id):
return "This is an edit form for blog entry no. %d" % int(entry_id)
dispatcher = None
def setup_routes():
d = cherrypy.dispatch.RoutesDispatcher()
d.connect('blog', 'myblog/:entry_id/:action', controller=BlogController())
d.connect('main', ':action', controller=MainController())
dispatcher = d
return dispatcher
conf = {
'/': {
'request.dispatch': setup_routes()
},
'/static': {
'tools.staticdir.on': True,
'tools.staticdir.root': os.path.dirname(os.path.abspath(__file__)),
'tools.staticdir.dir': 'static'
}
}
def start(config=None):
if config:
cherrypy.config.update(config)
app = cherrypy.tree.mount(None, config=conf)
cherrypy.quickstart(app)
if __name__ == '__main__':
start(os.path.join(os.path.dirname(__file__), 'app.conf'))
}}}
To make use of Routes url-generation, we can for example call {{{cherrypy.url(routes.url_for(controller="blog", action="edit", entry_id="123"))}}} - which generates an url that goes straight to the edit form of blog entry 123. It might be something like {{{http://www.example.org/my_cp_app/myblog/123/edit}}}.
Calling {{{cherrypy.url(routes.url_for(...))}}} is tedious, especially for a template designer, so let's make things easier:
My goal here is to provide one function - {{{url}}} - which transparently handles both the role of {{{cherrypy.url}}} and {{{routes.url_for}}}. In it's simplest form it looks like this:
{{{
#!python
import routes
import cherrypy
def url(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and type(args[0]) in (str, unicode):
return cherrypy.url(args[0])
else:
return cherrypy.url(routes.url_for(*args, **kwargs))
}}}
This makes it possible to do things like this
{{{
#!python
>>> url('/static/css/main.css')
'http://www.example.org/path_to_webapp/static/css/main.css'
>>> url(controller="main")
'http://www.example.org/path_to_webapp'
>>> url(controller="blog", action="edit", entry_id=123)
'http://www.example.org/path_to_webapp/myblog/123/edit'
}}}
I find the most common use is the last one, specify a controller, an action and some parameters. In most cases, a controller is a class instance and the action is a method. This makes me want to write something like this:
{{{
#!python
>>> url.blog.edit(entry_id=123)}
'http://www.example.org/path_to_webapp/myblog/123/edit'
}}}
Of course, this is easy in Python, with the following definition of {{{url}}}
{{{
#!python
import routes
import cherrypy
class _ctrlchain(object):
def __init__(self, name, head=None):
if head is None:
self.chain = list()
else:
self.chain = head[:]
self.chain.append(name)
def __getattr__(self, attr):
return _ctrlchain(attr, self.chain)
def __call__(self, *args, **kwargs):
if len(self.chain) > 3:
raise Exception("Don't know what to do with over 3 chain elements")
if len(self.chain) > 2:
kwargs["action"] = self.chain[2]
if len(self.chain) > 1:
kwargs["controller"] = self.chain[1]
if len(args) == 1 and len(kwargs) == 0 and type(args[0]) in (str, unicode):
return cherrypy.url(args[0])
else:
return cherrypy.url(routes.url_for(*args, **kwargs))
url = _ctrlchain('urlgen')
}}}
This correctly handles all the use cases above. For good measure, here are some more:
{{{
#!python
>>> url.main()
'http://www.example.org/path_to_webapp'
>>> url.blog(entry_id=123)
'http://www.example.org/path_to_webapp/myblog/123'
}}}
Of course, this becomes most useful when this function is made available in whatever template language you are using:
{{{
#!xml
<li py:for="entry in blog.select()">
<a href="${url.blog(entry_id=entry.id)}" py:content="entry.title">Placeholder for entry title</a>
</li>
}}}
For more information on Routes url generation check the Routes documentation: http://routes.groovie.org/module-routes.html#url_for
-- Arnar (arnarbi at gmail)
For more information about what kind of urls can be matched with Routes and how to do so, take a look at RoutesUrlMatching