It's not the same to POST to an URL than to GET it, DELETE it or PUT it. These actions are fundamentally different. However, Django seems to ignore them in its dispatch mechanism. Basically, one is forced to either ignore HTTP verbs completely or do this on every view:
def my_view(request, arg1, arg2):
if request.method == 'GET':
return get_view(request, arg1, arg2)
if request.method == 'POST':
return post_view(request, arg1, arg2)
return http.HttpResponseNotAllowed(['GET', 'POST'])
The few solutions I have found for this in the web (this snippet for verb-based dispatch, or this decorator for verb requirement) are not very elegant as they are clearly just workarounds.
The situation with CherryPy seems to be the same. The only frameworks I know of that get this right are web.py and Google App Engine's.
I see this as a serious design flaw for a web framework. Does anyone agree? Or is it a deliberate decision based on reasons/requirements I ignore?
I can't speak for Django, but in CherryPy, you can have one function per HTTP verb with a single config entry:
request.dispatch = cherrypy.dispatch.MethodDispatcher()
However, I have seen some situations where that's not desirable.
One example would be a hard redirect regardless of verb.
Another case is when the majority of your handlers only handle GET. It's especially annoying in that case to have a thousand page handlers all named 'GET'. It's prettier to express that in a decorator than in a function name:
def allow(*methods):
methods = list(methods)
if not methods:
methods = ['GET', 'HEAD']
elif 'GET' in methods and 'HEAD' not in methods:
methods.append('HEAD')
def wrap(f):
def inner(*args, **kwargs):
cherrypy.response.headers['Allow'] = ', '.join(methods)
if cherrypy.request.method not in methods:
raise cherrypy.HTTPError(405)
return f(*args, **kwargs):
inner.exposed = True
return inner
return wrap
class Root:
@allow()
def index(self):
return "Hello"
cowboy_greeting = "Howdy"
@allow()
def cowboy(self):
return self.cowboy_greeting
@allow('PUT')
def cowboyup(self, new_greeting=None):
self.cowboy_greeting = new_greeting
Another common one I see is looking up data corresponding to the resource in a database, which should happen regardless of verb:
def default(self, id, **kwargs):
# 404 if no such beast
thing = Things.get(id=id)
if thing is None:
raise cherrypy.NotFound()
# ...and now switch on method
if cherrypy.request.method == 'GET': ...
CherryPy tries to not make the decision for you, yet makes it easy (a one-liner) if that's what you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With