Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django get url regex by name

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/.

like image 960
freakish Avatar asked Sep 06 '13 08:09

freakish


People also ask

How Django URLs work with regular expressions?

We use regular expressions to match these url paths to their corresponding request handler (aka View). If a path, either dynamic or static, is matched the View will then handle the request and return a response to the User. If a path is not matched, Django will automatically send a 404 Page Not Found response.

What is name in Django URLs?

Django offers a way to name urls so it's easy to reference them in view methods and templates. The most basic technique to name Django urls is to add the name attribute to url definitions in urls.py .

What is Re_path in Django?

re_path is an implementation of the 'old' way of handling urls, which was previously (version <2) done by url from django. conf. urls . See the paragraph in Django 2.0 release notes about this.


2 Answers

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.

like image 64
Adrián Avatar answered Oct 03 '22 07:10

Adrián


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.

like image 43
freakish Avatar answered Oct 03 '22 06:10

freakish