Updated answer in 2016: Modern Django has everything necessary built-in and available through the class-based views. In the most raw form, the canonical approach is subclasssing django.views.generic.View
and implementing class methods that are named after the HTTP verbs:
class MyView(View):
def get(self, request, *args, **kwargs):
# ...
def post(self, request, *args, **kwargs):
# ...
Internally, this works in a way very similar to my ancient code below (which was written before Django had class-based views). There is a View.dispatch
method that basically looks up what to call, or return 405 if it can't find anything: getattr(self, request.method.lower(), self.http_method_not_allowed)
.
Of course, if you do form processing, template rendering or common CRUD stuff, be sure to check out the available View
subclasses.
Legacy answer from 2009 below. The code still works in 2016, but isn't a DRY solution, so don't use it. In 2011 Django got class-based views and nowadays they're the standard way how things should be done. I'm keeping this here solely for historical purposes. Old answer text follows:
In one particular view where I need to have separate code for different HTTP methods (this is my tiny WebDAV implementation), I'm doing something like this:
class SomeView(object):
def method_get(self, request, ...):
...
def __call__(self, request, *args, **kwargs):
m = getattr(self, 'method_%s' % request.method.lower(), None)
if m is not None:
return m(request, user, *args, **kwargs)
return HttpResponseNotAllowed("405 Method Not Allowed")
# Then url(r'...', SomeView()),
Added/edited: Well, I've thought a bit and actually implemented decorator approach. It is not as bad as I initially thought.
def method_not_allowed_view(request, *args, **kwargs):
return HttpResponseNotAllowed("405 Method Not Allowed")
def http_method(*methods):
methods = map(lambda m: m.lower(), methods)
def __method_wrapper(f):
this_module = __import__(__name__)
chain = getattr(this_module, f.__name__, method_not_allowed_view)
base_view_func = lambda request, *args, **kwargs: \
f(request, *args, **kwargs) if request.method.lower() in methods \
else chain(request, *args, **kwargs)
setattr(this_module, f.__name__, base_view_func)
return base_view_func
return __method_wrapper
@http_method('get')
def my_view(request):
return HttpResponse("Thank you for GETting.")
@http_method('post', 'put')
def my_view(request):
return HttpResponse("Thank you for POSTing or PUTting.")
# url(r'...', 'app.my_view'),
This post is a community wiki, anyway, so feel free to improve if you like the idea! And the revision history also contains some a bit different approaches I tried before writing this...