31

I'm having some difficulties finding information about this, probably it's not the right approach. I'd like to route a request to two different view functions based on the http method (GET or POST or DELETE or PUT).

As it is usually done in REST apis, this would mean that the same url has different meaning based on the HTTP method.

I don't see a way to do this in the urls.py file of django, I'd like something like:

url(r'^tasks$', 'app.views.get_tasks', method='get'),
url(r'^tasks$', 'app.views.create_task',  method='post'),

(note: I'm working with django 1.4)

luca
  • 12,311
  • 15
  • 70
  • 103
  • 1
    possible duplicate of [How can you dispatch on request method in Django URLpatterns?](http://stackoverflow.com/questions/2964334/how-can-you-dispatch-on-request-method-in-django-urlpatterns) – luca Sep 30 '13 at 14:09

3 Answers3

30

I don't think you can do this with different functions without adding a bunch of logic to the URL (which is never a good idea), but you can check inside the function for the request method:

def myview(request):
    if request.method == 'GET':
        # Code for GET requests
    elif request.method == 'POST':
        # Code for POST requests

You could also switch to class-based views. You would then only need to define a method for each of the HTTP methods:

class CreateMyModelView(CreateView):
    def get(self, request, *args, **kwargs):
        # Code for GET requests

    def post(self, request, *args, **kwargs):
        # Code for POST requests

If you decide to go the class-based route, another good resource is http://ccbv.co.uk/.

hellsgate
  • 5,905
  • 5
  • 32
  • 47
18

Because Django allows you to use callables in url config, you can do it with a helper function.

def method_dispatch(**table):
    def invalid_method(request, *args, **kwargs):
        logger.warning('Method Not Allowed (%s): %s', request.method, request.path,
            extra={
                'status_code': 405,
                'request': request
            }
        )
        return HttpResponseNotAllowed(table.keys())

    def d(request, *args, **kwargs):
        handler = table.get(request.method, invalid_method)
        return handler(request, *args, **kwargs)
    return d

To use it:

url(r'^foo',
    method_dispatch(POST = post_handler,
                    GET = get_handler)),
Juri Pakaste
  • 1,402
  • 10
  • 15
  • Quite a neat approach, had considered doing this myself, thanks for sharing – SleepyCal Jun 13 '14 at 15:42
  • 4
    Even though it's a great solution, keep in mind that middlewares can try to access the "callback" (view), like the `django.middleware.csrf.CsrfViewMiddleware` does to check if `csrf_exempt` is present, and by giving a callback that is not a Django view you can have unexpected behaviours in your system. – Marcelo Sep 27 '18 at 19:16
1

For Django 3.2 you can use require_http_methods

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

See https://docs.djangoproject.com/en/3.2/topics/http/decorators/#django.views.decorators.http.require_http_methods for details

Kim Stacks
  • 10,202
  • 35
  • 151
  • 282