Including a querystring in a django.core.urlresolvers reverse() call

I'm trying to reverse a named URL and include a querystring in it. Basically, I've modified the login function, and I want to send ?next= in it.

Here's what I'm doing now: reverse(name) + "?next=" + reverse(redirect)

Here's what I'd like to do: reverse(name, kwargs = { 'next':reverse(redirect) } )

My URL for the login page (just as an example) looks like this:

url(r'^login/', custom_login, name = 'login'),

So how do I modify this whole thing (or call it) to include the next without having to concatenate it? It feels like an iffy solution at best.


Solution 1:

You can't capture GET parameters in the url confs, so your method is correct.

I generally prefer string formatting but it's the same thing.
"%s?next=%s" % (reverse(name), reverse(redirect))

http://docs.djangoproject.com/en/dev/topics/http/urls/#what-the-urlconf-searches-against

The URLconf searches against the requested URL, as a normal Python string. This does not include GET or POST parameters, or the domain name.

Solution 2:

I just made my own utility function like the one asked in the question:

from django.utils.http import urlencode

def my_reverse(viewname, kwargs=None, query_kwargs=None):
    """
    Custom reverse to add a query string after the url
    Example usage:
    url = my_reverse('my_test_url', kwargs={'pk': object.id}, query_kwargs={'next': reverse('home')})
    """
    url = reverse(viewname, kwargs=kwargs)

    if query_kwargs:
        return f'{url}?{urlencode(query_kwargs)}'

    return url

Solution 3:

I think it's better to wrap Django's reverse method to expose this API. Here's some simple code to do it:

from django.core.urlresolvers import reverse as django_reverse

def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current_app=None):
    """
    Wrapper of django.core.urlresolvers.reverse that attaches arguments in kwargs as query string parameters
    """
    if kwargs:
        return '%s?%s' % (django_reverse(viewname, urlconf, args, None, prefix, current_app), \
                        '&'.join(['%s=%s' % (k,v) for k,v in kwargs.items()]))
    else:
        return django_reverse(viewname, urlconf, args, kwargs, prefix, current_app)

Put this code into some utility or common app that only depends on Django, then instead of importing django.core.urlresolvers.reverse just import myproject.myutils.urlresolvers.reverse