I did some googling and really couldn't find an answer to this.
Let's say I have the following simple class:
class dashboard:
def index(self, request):
return HttpResponse("Hello world")
def profile(self, request):
return HttpResponse("Your profile")
def user(self, request, user_id):
usr = User.objects.get(id=user_id)
return HttpResponse("Some info on usr")
What I want to do is map:
mysite.com/dashboard => dashboard.index
mysite.com/dashboard/profile => dashboard.profile
mysite.com/dashboard/user/1 => dashboard.user(1)
Without having to have a URL pattern for each URL - such that:
urlpatterns = patterns('',
url(r'^/dashboard$', views.dashboard, name='dashboard'),
url(r'^/dashboard/profile$', views.profile, name='profile'),
# etc...
)
I thought about this and the only I could think this could work is:
urlpatterns = patterns('',
url(r'^/(?<root1>\w+)/(?<root2>\w+)/(?<root3>)$', urltrigger, name='trigger'),
# etc...
)
# https://stackoverflow.com/questions/547829/how-to-dynamically-load-a-python-class
def my_import(name):
mod = __import__(name)
components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
def urltrigger(request, root1, root2 = None, root3 = None):
try:
cmodule = my_import(root1)
dync = cmodule() # perhaps request is a parameter to the constructor?
if root2 = None:
dync.index(request)
else:
method = getattr(dync, root2)
if root3 == None:
method()
else:
method(root3)
except:
raise Http404
(Excuse the quickly done Python code) This really shouldn't introduce any security issues as the class module has to exist in pythonpath - and if an attacker does have access to files in pythonpath they could just as easily modify the other views/code.
Does anyone have any thoughts on this? Using this approach I would probably lose the ability to use Django decorators - which I'm ok with as I could mimic the behavior of decorators I would want (such as ensuring a person is logged in and/or is in specific groups) in the constructor of the class.
Edit:
I got it all working - it was only like 10 lines of code.
Edit2:
For those wondering:
Put this as a pattern:
(r'^(.+?)/(?:(.+?)/)?$', free_pattern)
And here is free_pattern (I've obviously modified it since then):
def free_pattern(request, view, segments=""):
if segments != None:
segments = segments.split(r'/')
match = re.match("(\.|\\|/)", view)
if match:
raise Http404
dnyc = None
try:
if not segments:
cmodule = __import__(view + ".controllers." + view, globals(), locals(), [view], -1)
dnyc = getattr(cmodule, view)
else:
cmodule = __import__(view + ".controllers." + segments[0], globals(), locals(), [segments[0]], -1)
dnyc = getattr(cmodule, segments[0])
except Exception, e:
return HttpResponse(str(e))
c = dnyc(request)
if segments == None or len(segments) == 1:
return c.index()
else:
lst = segments[2:]
return getattr(c, segments[1])(*lst)
What that will do is if you go to /site/ it will load the class site in site/controllers/site.py and call the index method:
class site:
def __init__(self, request):
pass # do something with request
def index(self):
pass
if you go to /site/page it will load the class page in site/controllers/page.py and call the index method. Going to /site/page/one will invoke the one method in class page, and finally going to /site/page/one/1/ will pass the 1 (and anything after it) as a parameter as parameters to the method.
If you don't think this would work - tell me why, not "I'm really glad I would never have to support that". The later doesn't help me, doesn't help other people and doesn't explain why such as a system wouldn't work. In my opinion - a comment alone will not answer that.
If you are wondering I'm stuck on Django 1.4 because I must use Python 2.5 without virtualenv/pip/easy_install. I really want to - but it's non-negotiable. This isn't just for my "personal" use either - so switching a web host is also out of the question.
One thing I want to note - using official Django CBV is only introducing the same problems (ignoring security) - such as inability to apply Django decorators such as login_requried. One can see this SO post ( https://stackoverflow.com/a/11525851/195722 ) for a work around.