In TG1 we allowed you to either pass kwargs for extra url parmeters or
to pass a params dictionary as a keyword argument, or as the second
positional parameter, so that got around the reserved keyword
arguments problem. Perhaps routes could do something similar...
Well, the current method is to pass in
"http://mydomain.com/blog/newposts" which will generate the URL you
want.
--Mark
Right, this is why Pylons will have a redirect() function that does
not pass the args through redirect_to. It's included in the latest
Pylons 0.9.7 tip code (soon to be released as final), but isn't yet
used as the default as we wanted the book to be accurate for at least
one released version of Pylons. :)
Cheers,
Ben
> Another issue with url_for is the handling of query string parameters
> using keyword arguments. Currently any extra keyword arguments will be
> added to the generated URL as query string, for example:
>
> url_for('/page', arg='val') ==> /page?arg=val
>
> The problem is that you can't use keyword arguments for any of the
> argument names reserved by the function, such as "host", "protocol",
> etc, for example:
>
> params = { 'arg': 'val', 'host': 'machine' }
>
> url_for('/page', **params)
>
> should return:
>
> /page?arg=val&host=machine
>
> but it treats the "host" key in params as the host name so it actually
> returns:
Right, the alternative we thought of was forcing all the keyword args
destined for Routes to be in a dict passed in, but that obviously
makes it a little more annoying to use, though it keeps the arguments
separate from the ones url_for itself uses. However, I could see
having it strip off arg's with a trailing underscore for use by
Routes, such that you'd be able to do:
url_for('/page', host_='machine') -> /page?host=machine
Would that work?
Cheers,
Ben
The trailing undercore syntax is already implemented for dealing with
Python reserved words.
$ paster shell development.ini
>>> from routes import url_for
>>> from pylons import url
>>> url_for("ABC", print_=1)
'ABC?print_=1'
>>> url("ABC", print_=1)
'ABC?print_=1'
>>> url_for("ABC", host_="ab")
'ABC?host=ab'
>>> url("ABC", host_="ab")
'ABC?host=ab'
>>> url_for("ABC", host="example.com")
'http://example.comABC'
>>> url("ABC", host="example.com")
'http://example.comABC'
>>> url_for("ABC", host="example.com/")
'http://example.com/ABC'
>>> url("ABC", host="example.com/")
'http://example.com/ABC'
The last one is a bit surprising, that the 'host' argument needs a
trailing slash, but otherwise it seems to work fine and just needs to
be better documented.
> params (dict) - used to create the query string
That is an interesting idea. I'm not sure if I favor it or not. It
would mean I'd have to change an application I'm about to put into
production. But more importantly, the url_for tradition has been that
arguments not matching path variables are turned into query
parameters. If an expected variable is missing, that makes it obvious
what the problem is if you know what to look for. On the other hand,
I suppose an exception would make it even more obvious.
Part of the tentative plan for Routes 2 is to turn the extra variables
in route definitions into a dict, for the same reason as is suggested
here. I tried it and ended up being lukewarm to the idea. (Do I
really want every route to have 'map.connect("name", "/path",
{"controller": "foo", "action": "fooaction"}'?) On the other hand,
eliminating ambiguity is an important goal.
> Actually, I think I like the idea of making the query params into a
> single argument. It could be a string like a=1&b=2 or a dict.
No! Encouraging the user to do their own interpolation and escaping
would be a step back into the Dark Ages.
It's like the old rails select() helper which took an HTML snippet of
options rather than a list.
--
Mike Orr <slugg...@gmail.com>
Ah, that's a good point. (Although it has been this way for years and
nobody has complained about it till now, so I guess using 'host' as a
bona fide parameter is not very common.)
So what about having a 'params' argument but keeping the current
behavior if 'params' is not set. That would allow backward
compatibility.
Then if 'params' is set and another argument doesn't correspond to any
path variable, I guess you'd raise an error.
Wyatt wrote wrote:
> > > Actually, I think I like the idea of making the query params into a
> > > single argument. It could be a string like a=1&b=2 or a dict.
Mike wrote:
> > No! Encouraging the user to do their own interpolation and escaping
> > would be a step back into the Dark Ages.
Wyatt wrote:
> Sorry if I'm being daft, but you're only vehemently (!) opposing
> ``params`` as a *string*, correct?
Correct.
Wyatt wrote:
> I see your point there, and I can't think of a case where I'd need or
want to use a str instead of a dict--except maybe where the query
string came from elsewhere, but that seems like a fairly unusual case.
Almost always they will come from request.params, which has already
parsed them into a dict. Perhaps reading a log file or something.
But in general, the parsing to a dict and back is a feature of any
good framework, even if it does add some overhead. But that kind of
overhead is why you're using a framework in the first place.
--
Mike Orr <slugg...@gmail.com>
Created Routes ticket #88 for this.
http://routes.groovie.org/trac/routes/ticket/88
--
Mike Orr <slugg...@gmail.com>
> right and don't forget about adding the new "raw" (to avoid mapper
> lookup if true) and "external" (to avoid adding SCRIPT_NAME prefix if
> true) arguments, in addition to the "params" dict, so you will have a
> much better url_for
One of your issues with proto, and host, was that you couldn't then
use them in Routes names. By adding three more, it means you can't have:
map.connect('/host/{raw}/{params}'), etc.
In addition to not having host, proto, etc.
So the routing section for Pylons users gets a longer list of reserved
words that can't be used, and host still can't be used. Was that the
desired result?
Cheers,
Ben
Why are those names not allowed in the path? The issue isn't one of
variable collisions but keyword arg collisions.
Although I'm still not sure of the intrinsic merits of the 'raw' and
'external' proposals, which is why I wanted your opinion. In Routes
2, 'external' is marked at route definition, not in the URL call. And
paths that contain a colon before any slash ("http:") are
automatically marked external. So perhaps we want to avoid piling
kludge upon kludge in Routes 1 to accomplish the same thing, which is
my concern about 'raw' and 'external'.
--
Mike Orr <slugg...@gmail.com>
(1) would be solved by separating route lookups from literal URL
generation, for which Routes 2 has suggested something like
url.literal("/path", **params).
(2) is really a variation of an external URL, so I'm hesitant to add a
keyword arg just for it.
(3) is covered by the 'params' proposal.
Routes has several problems. Some of these have to be dealt with by
not using url_for() in certain edge cases, but just using a literal
URL. My concern is a long-term solution. If we add 'raw' and
'external' now, it will be a temporary kludge until Routes is
restructured, and then we'll either have to support it forever or
abandon it. Supporting it means bloated code and a convoluted API;
abandoning it means people would have to change their applications.
Vs the status quo of just not using url_for in certain situations.
The advantage of that is we aren't *adding* to the number of changes
people may have to make in the future when a long-term solution is
reached.
--
Mike Orr <slugg...@gmail.com>