Starlette: add support to request.url_for for query parameters

Created on 21 Jun 2019  路  9Comments  路  Source: encode/starlette

I totally misunderstood url_for's API, I was expecting url_for('some_route', foo='bar') to return 'http://testserver/my_route/?foo=bar'.

I understand why that is now, but what is the best way to generate urls like this?
(for pagination in a rest list api in this case).

Could url_for grow support for query parameters in addition to path parameters?

Most helpful comment

are there any updates?

All 9 comments

Totally agree with you, it would be very convenient.

I propose to add all unmatched path params to query string.

I think what we'll probably do here is have url_for return a URL instance, and add a .with_query_params method to URLs, which would allow for this kind of usage...

url = request.url_for('users', username="tom").with_query_params(...)

Sounds great, but would there be a less wordy alternative?

Perhaps just this...

  • request.url_for('users', username="tom").with_query(...)

Or an explicitly shorthand notation...

  • request.url_for('users', username="tom").q(...)

.params() maybe?

We probably want to differentiate it more clearly from the properties on the URL class. (.params might just as well indicate a property on the class. .with_query() makes it kinda clear that it's going to return a new URL.)

I think we probably want to end up with...

  • .query_params - A QueryParams datastructure.
  • .query_string - A raw string.
  • .with_query(...) - A new URL.

@cjw296 @tomchristie

I see you're taking some care to think this through, so I'll be closing my open PR on this soon (been open for more than a month now) - the approach I took in the PR was more akin to the existing implementation. But I guess you want to refactor in order to provide a more proper support for the URL's query. And I'm defintely :+1: on that

are there any updates?

I implemented a naive workaround to this:

def url_for_query(request: Request, name: str, **params: str) -> str:
    url = request.url_for(name)
    parsed = list(urllib.parse.urlparse(url))
    parsed[4] = urllib.parse.urlencode(params)
    return urllib.parse.urlparse(parsed)

templates = Jinja2Templates(directory='templates')
templates.env.globals['url_for_query'] = url_for_query

Then, in template

{ url_for_query('foo', bar='baz') }

This solution doesn't work for routes that accept a parameter.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rlewkowicz picture rlewkowicz  路  3Comments

berislavlopac picture berislavlopac  路  5Comments

tomchristie picture tomchristie  路  5Comments

cw-andrews picture cw-andrews  路  4Comments

koddr picture koddr  路  6Comments