Reputation: 56477
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/
.
Upvotes: 12
Views: 5549
Reputation: 6392
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))
Upvotes: 1
Reputation: 56477
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.
Upvotes: 2
Reputation: 20123
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
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.
Upvotes: 0
Reputation: 2482
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]
Upvotes: 1
Reputation: 6255
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.
Upvotes: 2