12

I have a case where I have defined some Django url patterns and now I want to retrieve the regular expression associated with a given pattern. I want that because I want to pass these regular expressions to the client so I can check urls in client as well ( I'm talking about browser side history manipulation ) and fire appropriate handlers ( in JavaScript ) when there is a match.

For example if I have:

# urls.py
urlpatterns = patterns("",
    url(r"^$", Index.as_view(), name="index"),
    url(r"^user/", include("User.urls", namespace="User")),
)

# User/urls.py
urlpatterns = patterns("",
    url(r"^profile/(?P<slug>.*)$", GetProfile.as_view(), name="get_profile")
)

then I need the following function:

>>> get_regex("User:get_profile")
'^user/profile/(?P<slug>.*)$'

( or however Django translates it ). Note that I'm using namespaces. Any ideas? Django1.5.

Also I've managed to write a function that returns the urlpattern object associated with a passed name, however doing url.regex.pattern returns '^profile/(?P<slug>.*)$. So as you can see there is no leading ^user/.

5
  • can you show a piece of the views.py it could be helpful Commented Sep 6, 2013 at 8:40
  • @drabo2005 How would that be helpful? The content is irrelevant. Commented Sep 6, 2013 at 8:42
  • @freakish Just a pointer: if you look at the RegexURLResolver class in django.core.urlresolvers.py, it has a reversed_dict property. It should have the pattern you're looking for. Have a look at line 385, same file. Commented Sep 6, 2013 at 8:56
  • Have you tried ^user/profile/(?P<slug>[^/]+)/$ Commented Sep 6, 2013 at 9:03
  • @hwnd I'm trying to avoid writing regular expressions manually (too many views). Commented Sep 6, 2013 at 9:31

5 Answers 5

2

There are several javascript reverse implementations out there.

http://djangojs.readthedocs.org/en/latest/djangojs.html#reverse-urls

https://github.com/version2/django-js-reverse

It's not the regex, but you could test the urls in your client code just like you do in the server, so it's even better in my opinion.

EDIT: Since you need to ignore URL arguments, you could get an idea from the source of django-js here. It already removes optional URL arguments, so it's probably very similar to what you describe.

The code iterates over every pattern removing the ?P from each argument subregex so you could just replace them with .*.

The point is you have in that source every regex you could possibly need to do your implementation. See the global patterns in lines 24-29.

Sign up to request clarification or add additional context in comments.

8 Comments

That's not my case. I'm not trying to reverse URLs. I'm trying to get regex so I can fire a handler if a URL matches the regex in JavaScript.
But what is the difference? Can't you just do if ( url === reverse('name') )
The difference is that url can have additional args like slug in the example I've shown above. So given url is (for example) /user/profile/root and what will you call reverse with? I need to match it with regex.
There is a django command to list all url patterns, I guess its called urls or show_urls. You could take a look at implementation of this command. This question might help stackoverflow.com/questions/1828187/…
@freakish have a look at their source in this file
|
2

So I've tried few things and finally I came up with my own solution. First I convert urlpatterns into a form which JavaScript understands:

import re
converter = re.compile(r"\?P<.*?>")

def recursive_parse(urlpatterns, lst):
    for pattern in urlpatterns:
        obj = {
            "pattern": converter.sub("", pattern.regex.pattern)
        }
        if hasattr(pattern, "name") and pattern.name is not None:
            obj["name"] = pattern.name
        if hasattr(pattern, "namespace"):
            obj["namespace"] = pattern.namespace

        if hasattr(pattern, "url_patterns"):
            if "urls" not in obj:
                obj["urls"] = []
            recursive_parse(pattern.url_patterns, obj["urls"])

        lst.append(obj)


def generate_paths(urlpatterns):
    paths = []
    recursive_parse(urlpatterns, paths)
    return paths

Then I call generate_paths(urlpatterns), JSON-stringify the result and pass it to JavaScript (note that in JavaScript I have to convert regular expressions as strings to RegExp objects). In JavaScript I have

var recursive_check = function(url, patterns, names, args) {
    var l = patterns.length;
    for (var i = 0; i < l; i++) {
        var pat = patterns[i],
            match = pat.pattern.exec(url);
        pat.lastIndex = 0;
        if (match) {
            names.push(pat.namespace || pat.name);
            var f = match.shift(),
                url = url.replace(f, ""),
                ml = match.length;
            for (var j = 0; j < ml; j++) {
                args.push(match[j]);
            }
            if (pat.urls) {
                recursive_check(url, pat.urls, names, args);
            }
            break;
        }
    }
};

var fire_handler = function(url) {
    var names = [], args = [];
    recursive_check(url, patterns, names, args);
    // do something...
};

Now in // do something... I can do something with names and args. For example I can keep a dictionary of named handlers, I can search for a handler (based on names) and call it with args.

That's the solution that works for me. Converting urlpatterns to JavaScript patterns might not be perfect (since converter seems to be a bit too simplified) but it works in most simple cases.

Comments

1

Try this:

from django.core.urlresolvers import get_resolver

resolver = get_resolver(None)
url = resolver.reversed_dict.getlist('get_profile')
if url:
    pattern = url[0][1]

3 Comments

First of all: resolver.reverse_dict gets populated when reverse is called ( which might not happen at all in my case ). Secondly: the same issue: it does not include entire url pattern (prefix ^user/ will be missing, because of url(..., include(...))).
@freakish I tried to run the code in my shell and I got the entire pattern, with the prefix. I don't get the ^ though. Are you sure that reverse_dict is not populated lazily whenever it gets called?
Actually I couldn't populate it at all with get_profile route. Are you sure you are using urlpatterns with include? This resolver object seems to keep only top level url patterns. The problem is with patterns which are included in other patterns.
1

Not an answer but might be useful to someone else looking at this.

The following generates a list of all, complete url patterns in the Django project, including for nested URLRegexResolvers, based on @Freakish's code.

import re
from django.core.urlresolvers import get_resolver

converter = re.compile(r"\?P<.*?>")

def trim_leading_caret(s):
    return s[1:] if s.startswith('^') else s

def recursive_parse(urlpatterns, lst, prefix=None):
    for pattern in urlpatterns:
        path = (prefix or '') + trim_leading_caret(converter.sub("", pattern.regex.pattern))
        if hasattr(pattern, "url_patterns"):
            recursive_parse(pattern.url_patterns, lst, path)
        else:
            lst.append('^' + path)

def generate_paths(urlpatterns):
    paths = []
    recursive_parse(urlpatterns, paths)
    return paths

generate_paths(get_resolver(None))

Comments

0

As far as I understood, you want to be able to return the regex expression (and not the url) of a given view.

This is my sketch of solution:

The function url returns an instance of RegexURLResolver. This class does store the regex, because it calls LocaleRegexProvider on __init__ (in this line and this line).

So, I think that you can

  1. reverse search the view plus namespace
  2. get the tuple of that view from the tuple of tuples urlpatterns
  3. return _regex of the first argument, LocaleRegexProvider._regex (or regex()), of the tuple respective to the view.

I'm not sure this works (didn't tested), neither that it is the best solution, but at least you have some links on where Django stores the regex.

1 Comment

Yep, that's pretty much what I meant by "I've managed to write a function" part. Unfortunetly _regex (which seems to be the same as .regex.pattern) only holds info about it's own regular expression (defined in url(...)), not the entire path (it does not include url(..., include(...))).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.