HTTP verbs are not used to determine the route to a view method by deliberate design in Django. Sometimes I find it useful to be able to specify different methods for the same url pattern - one per HTTP verb. The Django book contains an interesting example of how this can be done using the django.conf.urls.defaults.url method to separate POST from GET processing. I’ve extended the example to provide handling to cover the standard HTTP verbs.
Listing: router.py
from django.http import Http404, HttpResponseNotAllowed
from django.conf.urls.defaults import url
GET = 'GET'
PUT = 'PUT'
POST = 'POST'
HEAD = 'HEAD'
TRACE = 'TRACE'
DELETE = 'DELETE'
OPTIONS = 'OPTIONS'
HTTP_METHODS = (GET, POST, PUT, HEAD, DELETE, OPTIONS, TRACE)
def dispatch(request, *args, **kwargs):
view = kwargs.pop(request.method, None)
if view:
for method in HTTP_METHODS:
kwargs.pop(method, None)
return view(request, *args, **kwargs)
else:
allowed_methods = []
for method in HTTP_METHODS:
if kwargs.pop(method, None):
allowed_methods.append(method)
if allowed_methods:
return HttpResponseNotAllowed(allowed_methods)
else:
raise Http404
def mapping(regex, viewname, get = None, post = None, put = None, delete = None, \
head = None, options = None, trace = None):
views = { GET: get, POST: post, PUT: put, DELETE: delete, HEAD: head, \
OPTIONS: options, TRACE: trace }
return url(regex, dispatch, views, viewname)
Listing: urls.py
from django.conf.urls.defaults import *
from router import mapping
import views
urlpatterns = patterns('',
(r'^$', views.index),
mapping(r'^document/(?P<document_id>[A-Za-z0-9]+)/$', 'single_document',
views.single_document_get, views.single_document_post),
)
Works for me.
UPDATE: There is also a more object-oriented/RESTful way to do this, as outlined in my Django RESTful resources blog post.