views:

93

answers:

2

I come from a Cake background, and I'm just starting to learn Django now. I'm liking it quite a bit, but I kinda wish it used convention over configuration like cake does. So,

  1. How can I get Cake-style URLs automatically? For example, if I went to mysite.com/posts/view/5 it would load up mysite.posts.views.view and pass an argument 5 to it? I was thinking I could add something like (r'^(.*)/(.*)', 'mysite.$1.$2'), to urls.py, but of course, that won't work.

  2. How can I automatically load up a template? Each view function should automatically load a template like templates/posts/view.html.

Is this even possible, or do I have to hack the core of Django?


Here's my solution, based on what Carl suggested:

urlpatterns = patterns('',
    # url pats here
    url(r'^(?P<app>\w+)/(?P<view>\w+)/(?P<args>.*)$', 'urls.dispatch')
)

def dispatch(req, app, view, args): # FIXME: ignores decorators on view func!
    func = get_callable(app+'.views.'+view)
    if args:
        ret = func(req, *args.split('/'))
    else:
        ret = func(req)
    if type(ret) is dict:
        return render_to_response(app+'/'+view+'.html', ret)
    else:
        return ret

Seems to be working pretty well with initial tests. Solves both problems with a single function. Probably won't support GET-style arguments tho.

+1  A: 

I don't you'll need to hack the core of Django for this. It sounds like you might be in need of generic views. Also check out the Generic Views topic guide.

The first example given in the generic views documentation sounds like your first bullet point:

Example:

Given the following URL patterns:

urlpatterns = patterns('django.views.generic.simple',
    (r'^foo/$',             'direct_to_template', {'template':'foo_index.html'}),
    (r'^foo/(?P<id>\d+)/$', 'direct_to_template', {'template':'foo_detail.html'}),
)

... a request to /foo/ would render the template foo_index.html, and a request to /foo/15/ would render the foo_detail.html with a context variable {{ params.id }} that is set to 15.

Zack Mulgrew
Uhm.... I'm not quite sure how this addresses the issue at all? You've defined separate URL patterns for each..? I want it to be automatic.
Mark
Mark, I did misunderstand the question. I wasn't thinking you meant a single pattern that will map to your app, view, and pass a parameter. I thought we might have been talking about a single app.
Zack Mulgrew
+3  A: 

Those points are both implementable without hacking Django core, but either one will require a non-trivial level of familiarity with advanced Python techniques.

You can do the generic URL pattern with a pattern like this:

url(r'^(?P<appname>\w+)/(?P<viewfunc>\w+)/(?P<args>.*)$', 'myresolverfunc')

Then define a 'myresolverfunc' "view" function that takes "appname", "viewfunc", and "args" parameters, and implement whatever logic you want, splitting args on "/" and dynamically importing and dispatching to whatever view function is referenced. The trickiest part is the dynamic import, you can search Django's source for "importlib" to see how dynamic imports are done internally various places.

The automatic template loader can be implemented as a view function decorator similar to the various "render_to" decorators out there, except you'll generate the template name rather than passing it in to the decorator. You'll have to introspect the function object to get its name. Getting the app name will be trickier; you'll probably just want to hardcode it as a module-level global in each views.py file, or else work in conjunction with the above URL dispatcher, and have it annotate the request object with the app name or some such.

Carl Meyer
I could just grab the directory name, no? It's a bit unfortunate that I'd have to use a decorator. Isn't there any sort of "hook" that I can tap into? Cake has before and after filters you can use, or you can override the default controller...
Mark
Yeah, you could use __file__ and os.path.dirname and get the directory name, but I'd consider that uglier and more brittle than either of the options I gave. And what's unfortunate about a decorator? No reason for Django to provide Django-specific magical hooks when you can just use Python.
Carl Meyer
It's unfortunate because it requires me to add something to each of my functions, which could easily be forgotten. I wanted something that would automatically be applied to all view functions.
Mark
That kind of magical hook kills reusability of code. Now your views all depend on some nonstandard before/after hook also being executed, instead of being plain Python functions that take a request and return a response. Really; it's not worth saving the line of code.
Carl Meyer