Question

In ASP.NET MVC, you can use the AcceptVerbs attribute to correlate a view function with a verb:

public ActionResult Create()
{
    // do get stuff
} 

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection)
{
    // do post stuff
}

The Django Book suggests something like this:

def method_splitter(request, *args, **kwargs):
    get_view = kwargs.pop('GET', None)
    post_view = kwargs.pop('POST', None)
    if request.method == 'GET' and get_view is not None:
        return get_view(request, *args, **kwargs)
    elif request.method == 'POST' and post_view is not None:
        return post_view(request, *args, **kwargs)
    raise Http404

urls.py:

urlpatterns = patterns('',
    # ...
    (r'^somepage/$', views.method_splitter, {'GET': views.some_page_get, 
        'POST': views.some_page_post}),
    # ...
)

That seems a little ugly to me - is there a decorator that can associate an HTTP verb with a view, ASP.NET MVC-style, or another accepted way to do this?

Was it helpful?

Solution

There are standard built-in decorators for requiring particular HTTP method or list of allowed methods.

See the code: http://code.djangoproject.com/browser/django/trunk/django/views/decorators/http.py.

OTHER TIPS

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...

You can use View Decorators

From the docs:

from django.views.decorators.http import require_http_methods

@require_http_methods(["GET", "POST"])
def my_view(request):
    # I can assume now that only GET or POST requests make it this far
    # ...
    pass
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top