This is a rough little proof of concept to support decorating views with HTTP method names.
import sys
from django.http import HttpResponseNotAllowed
class Handler(object):
def __init__(self, name):
self._meth_to_func = {}
self._last_func = None
self.__name__ = name
self.func_name = name
def register(self, method, func):
self._meth_to_func[method] = func
self._last_func = func
def register_last(self, method):
self.register(method, self._last_func)
def __call__(self, request, *args, **kwargs):
method = request.method.lower()
if method in self._meth_to_func:
return self._meth_to_func[method](request, *args, **kwargs)
return HttpResponseNotAllowed([meth.upper() for meth in self._meth_to_func.iterkeys()])
class HttpMethodDecorator(object):
name_to_handler = {}
def __init__(self, method):
self.method = method.lower()
@classmethod
def get_handler(cls, name, scope):
if (name, scope) not in cls.name_to_handler:
cls.name_to_handler[(name, scope)] = Handler(name)
return cls.name_to_handler[(name, scope)]
def __call__(self, fun):
if isinstance(fun, Handler):
fun.register_last(self.method)
return fun
handler = self.get_handler(fun.__name__, sys._getframe().f_code)
handler.register(self.method, fun)
return handler
GET = HttpMethodDecorator('GET')
POST = HttpMethodDecorator('POST')
PUT = HttpMethodDecorator('PUT')
DELETE = HttpMethodDecorator('DELETE')
HEAD = HttpMethodDecorator('HEAD')
Sorry about the poor lack of doc comments, I whipped it up before leaving work today. usage:
from http_methods import GET, POST
@GET
def my_view(request):
...
return ...
@POST
def my_view(request):
...
return ...
@GET
@POST
def my_other_view(request):
...
return ...
Those two methods are supposed to have the same name. They will be added to a handler that switches to your correct function automatically.
In brehaut‘s opinion this is Too Much Magic for real world but an interesting exercise none the less.
angus publicly regrets putting Brehaut up to this, and plans to slink quietly away before anyone else notices.
Some alternatives
The following are some alternatives that various people have suggested.
- Django Rest Interface – Suggested by Marek Kuziel. Looks like a generally decent solution.
- Callable Class Views – Suggested by Dan Larkin. This is a very simple solution, does not handle the case that a method is not supported and is only available for GET and POST.
-
A more detailed snippet similar to Callable Class View is RestView. Note that this has a bug that it doesnt return a valid 405. The HTTP RFC specifies that an
Allowsheader must be returned. Cheers to angus for the pointer to this one.
