The patch adds a couple of things:
1. a new attribute in Application instances called named_handlers
2. a new method in Application called reverse_url, which given a name
and a set of arguments will attempt to create a url out of the looked up
"URLSpec" (I don't like the name URLSpec)
3. a new class URLSpec (again, I dislike the name), which creates a
format string representing the URL
based on the number of groups found in the pattern specified for
dispatching.
4. a new function added to templates: reverse_url(name, arg1, arg2...)
URLSpec acts like the tuples you pass now, so that there are minimal
changes to the add_handlers code in Application, and they do little work
up front to get this all to work.
URLSpec does have some limitations. The most major one being that
counting parenthesis isn't guaranteed to provide an accurate count on
the number of groups, so I expect this to work only on the simplest
dispatching url patterns.
Example usage taken from the blog demo:
from tornado.web import url
class Application(tornado.web.Application):
def __init__(self):
handlers = [
url(r"/", HomeHandler),
url(r"/archive", ArchiveHandler),
url(r"/feed", FeedHandler),
url(r"/entry/([^/]+)", EntryHandler),
(r"/compose", ComposeHandler),
(r"/auth/login", AuthLoginHandler),
(r"/auth/logout", AuthLogoutHandler),
]
Then in archive.html:
...
{% block body %}
<ul class="archive">
{% for entry in entries %}
<li>
<div class="title"><a href="{{ reverse_url("entry", entry.slug) }}">{{ escape(entry.title) }}</a></div>
<div class="date">{{ locale.format_date(entry.published, full_format=True, shorter=True) }}</div>
</li>
{% end %}
</ul>
{% end %}
Django does quite a bit, even going so far as to almost hand compile the regex
so that it can attempt to throw an error if the arguments you specify do not
correctly match the url you're trying to reverse. I'm not sure this is exactly
overkill as there is no reason to produce a url that won't actually be resolved,
and as a result, they only support a subset of urls that can be
reversed. It does
however, handle nested () correctly, which is a win.
Alkis' technique adds support for keyword args, which based on my
understanding aren't passed through to the handler as kwargs, correct? Is
there any plan for passing named capture groups as kwargs to request
handlers?
If so, it should be fairly simple to add what is needed to support that.
Thanks for checking this out!
How so? The only way I can see this being true is with a regular
expression that has
different groups based on positions... something like:
/(([a-z]+)|([0-9]+)([a-z]+))/
And even then, len(match.groups()) is 1 + the number of groups, in
left to right order,
meaning you'd have to have 3 positional arguments anyway. And
/((?P<word>[a-z]+)|(?P<number>[0-9]+)(?P<word>[a-z]+))/
is invalid, since there can't be multiple groups named the same thing.
Did you have some other reason in mind?
Is this a useful use case for webapps? (granted my experience is limited in this area). Any examples where it might be used?
After thinking it over again, I don't think this restriction will make it simpler.
The code that I used for reverse_url stores the format string in the
instance of the URLSpec, so that it computes it only once. Is this
what you mean by cachable? Or are you saying that Python could then
cache it somewhere?
Incidentally, I found this microbenchmark to be interesting. Using
named format string arguments adds quite a bit of overhead to string
creation:
$ python -m timeit -n 1000000 "'%(a)s %(b)s' % {'a': 'hello', 'b': 'world'}"
1000000 loops, best of 3: 0.902 usec per loop
$ python -m timeit -n 1000000 "'%s %s' % ('hello', 'world')"
1000000 loops, best of 3: 0.411 usec per loop
> 2009/10/19 Alkis Evlogimenos ('Αλκης Ευλογημένος) <evlog...@gmail.com>
>>
>> Is this a useful use case for webapps? (granted my experience is limited
>> in this area). Any examples where it might be used?
>> After thinking it over again, I don't think this restriction will make it
>> simpler.
>
>
> --
>
> Alkis
>
I'm not sure I follow...
Ah ha! I see what you mean now. Yeah, I guess that is problematic, and
that's probably
why Django converts it's format string to named parameters of the form
_N, where N
represents the positional group number. Sorry for the misunderstanding.
>
> The thing I like about Django urls is being able to name the url, so
> you aren't tying the actual url path in the template. So you could
> have something like
> (r'/entry-blah-blah/(\d+)/', EntryHandler, name = 'entry')
>
> and then in the template:
> {{ url('entry', '123') }}
>
> that way if you ever have to change the url, you don't have to go
> through and change all your templates as well. Thoughts?
Absolutely. +1
-Elias
In your handlers
handlers = [
url('/some/regex/([a-z]+)/', SomeHandler, name="some_handler"),
]
Then, {{ reverse_url('some_handler', 'arg1') }}. Note though that
there is no verification for parameters.
Woah... Upon looking back, I see that I made a mistake in what I
posted with the example:
class Application(tornado.web.Application):
def __init__(self):
handlers = [
url(r"/", HomeHandler, name="home"),
url(r"/archive", ArchiveHandler, name="archive"),
url(r"/feed", FeedHandler, name="feed"),
url(r"/entry/([^/]+)", EntryHandler, name="entry"),
(r"/compose", ComposeHandler),
(r"/auth/login", AuthLoginHandler),
(r"/auth/logout", AuthLogoutHandler),
]
Sorry about that!!