Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - get names of parameters needed to reverse url

Background

Let's say I have a url pattern with parameters that will link me to a view in django:

url(
    r'^things/(?P<thing_name>\w+)/features/(?P<feature_name>\w+)$',
    views.thingFeature,
    name='thing_feature'
),

And lets say I have a thing and a feature:

thing = Thing.objects.get(.....)
feature = thing.feature_set.first()

t_name = thing.name
f_name = feature.name

Now Django gives me the awesome ability to get a url that brings me to a page dedicated to a specific feature of a specific thing. I can do that like so:

from django.core.urlresolvers import reverse
url = reverse('thing_feature', thing_name=t_name, feature_name=f_name)
# url == '/things/thing2/features/left-arm'

Question

Now I've stumbled into a situation that I need to specifically address. I'm not looking for a workaround - I'm looking to solve the following problem:

Given a url's name, how do I get the list of kwarg argument names needed to reverse that url?

I am looking for the function get_kwarg_names_for_url. It behaves like so:

url_kwarg_names = get_kwarg_names_for_url('thing_feature')
# url_kwarg_names == ['thing_name', 'feature_name']

url_kwarg_names is now the list of every keyword I need to supply to Django's reverse function in order to reverse the url named "thing_feature".

Any help is appreciated!

Solution

Based on knbk's answer I was able to come up with the following solution:

def get_kwarg_names_for_url(url_name):
    resolver = get_resolver(get_urlconf())
    reverse_data = resolver.reverse_dict[url_name]

    pattern_list = reverse_data[0]

    '''
    Need to specify the 1st pattern because url regexes can
    potentially have multiple kwarg arrangments - this function does
    not take this possibility into account.
    ''' 
    first_pattern = pattern_list[0]

    '''
    `first_pattern` is now of the form `(url_string, kwarg_list)` -
    all we are interested in is the 2nd value.
    '''
    return first_pattern[1]
like image 473
Gershom Maes Avatar asked Oct 07 '15 21:10

Gershom Maes


2 Answers

I'll start with a fair warning: this isn't possible using the public API. On top of that, I'm actively working to rewrite the URL dispatcher for 1.10, so this method will most likely break by that time.

First, you need to get the right RegexURLResolver. If the view is not in a namespace, you can use the reverse_dict to get a list of possibilities, and extract the kwargs:

def get_kwargs(view_name):
    resolver = urlresolvers.get_resolver()
    patterns = resolver.reverse_dict.getlist(view_name)
    kwargs = []
    for possibility, pattern, defaults in patterns:
        for result, params in possibility:
            kwargs.append(params)
    return kwargs

Since a view name can have multiple patterns with different kwargs (though you'd want to avoid that for your own sanity), this will return a list of each set of possible kwargs. Usually the different sets would be the required kwargs on one side and required + optional kwargs on the other side.

I haven't tested this, but if it doesn't work you can dig around in resolver.reverse_dict for a bit to find out the exact specifics. It wasn't exactly designed with usability in mind.

like image 155
knbk Avatar answered Oct 02 '22 15:10

knbk


You should be able to do this with a resolve()

From the Docs:

A ResolverMatch object can then be interrogated to provide information about the URL pattern that matches a URL:

func, args, kwargs = resolve('/some/path/')

Specific to your example code:

url = reverse('thing_feature')
func, args, kwargs = resolve(url)
# args == ['thing_name', 'feature_name']
like image 34
Dan O'Boyle Avatar answered Oct 02 '22 16:10

Dan O'Boyle