views:

680

answers:

3

I have a pretty standard django app, and am wondering how to set the url routing so that I don't have to explicitly map each url to a view.

For example, let's say that I have the following views: Project, Links, Profile, Contact. I'd rather not have my urlpatterns look like this:

(r'^Project/$', 'mysite.app.views.project'),
(r'^Links/$', 'mysite.app.views.links'),
(r'^Profile/$', 'mysite.app.views.profile'),
(r'^Contact/$', 'mysite.app.views.contact'),

And so on. In Pylons, it would be as simple as:

map.connect(':controller/:action/:id')

And it would automatically grab the right controller and function. Is there something similar in Django?

+3  A: 
mods = ('Project','Links','Profile','Contact')

urlpatterns = patterns('',
   *(('^%s/$'%n, 'mysite.app.views.%s'%n.lower()) for n in mods)
)
Javier
That's giving me an error `'generator' object has no 'resolve'`. I think I know what you're trying to do (write a route for each string in the tuple), but it's creating a Generator that django can't resolve instead.
swilliams
i didn't test it, try unwrapping the generator with a *(...for...). i'm editing the answer
Javier
I'm getting a syntax error with this now. I've never seen * used like that... is that supposed to dereference a pointer like C? Could you please test this before posting?
swilliams
it expands an iterator to positional arguments. i did test it already.
Javier
Are you using some kind of library? What version of python? I get a syntax error on every kind of iterator object I try that on.
swilliams
no library, python2.5, but i don't think it's a new feature. try on command line: def f(a,b,c):print a,b,c f(1,*(2,3))
Javier
Got it to work. I had an extra ',' in there left over from the default definition after the statement that was causing syntax errors. It should be noted that the *() trick only appears to work as an argument. `foo = *(1,2)` causes a syntax error. I suspect it has to do with pointer dereferencing.
swilliams
it's not about pointers, '*(1,2)' expands the tuple (a single composite value) to two numbers, and you can't assign two numbers to a simgle variable.
Javier
This is completly unreadable, at least for me. I'd much more like to just look at code and see it then spend a second or to parsing it. Maybe I'm just built that way :)
kender
This is not a good solution.
hop
@{kender,hop}: yeah, i'd hate to see that too and wouldn't do myself. but swilliams wanted to, so it's there! :-)
Javier
Eh, I just really like the way Pylons does routing. Web.py does it even better in my opinion, though Django wins out for everything else.
swilliams
+4  A: 

Unless you have a really huge number of views, writing them down explicitly is not too bad, from a style perspective.

You can shorten your example, though, by using the prefix argument of the patterns function:

urlpatterns = patterns('mysite.app.views',
    (r'^Project/$', 'project'),
    (r'^Links/$', 'links'),
    (r'^Profile/$', 'profile'),
    (r'^Contact/$', 'contact'),
)
hop
This is true, but now I'm feeling comitted. :)
swilliams
+2  A: 

You might be able to use a special view function along these lines:

def router(request, function, module):
    m =__import__(module, globals(), locals(), [function.lower()])
    try:
        return m.__dict__[function.lower()](request)
    except KeyError:
        raise Http404()

and then a urlconf like this:

(r'^(?P<function>.+)/$', router, {"module": 'mysite.app.views'}),

This code is untested but the general idea should work, even though you should remember:

Explicit is better than implicit.

Mr Shark