Django: URL to specific request method
Asked Answered
I

3

6

I'm working with Django and I'M using class-based views with urls. So, in mu classes I have the methods: post, get, put and delete.

Example:

class MyClassView(View):
    def get(self, request, id=None):
        return HttpResponse('GET request')

    def post(self, request):
        return HttpResponse('POST request')

    def put(self, request, id):
        return HttpResponse('PUT request')

    def delete(self, request, id):
        return HttpResponse('DELETE request')

So in my URLs i have something like:

from django.urls import path
from . import views

urlpatterns =[
    path('my-class/', views.MyClassView.as_view()),
    path('my-class/<int:id>/', views.MyClassView.as_view()),
    path('my-class/create/', views.MyClassView.as_view()),
    path('my-class/update/<int:id>/', views.MyClassView.as_view()),
    path('my-class/delete/<int:id>/', views.MyClassView.as_view()),
]

This works fine! When I send a GET request to /my-class I get "GET request" and when a I send a POST request to /my-class/create I get "POST request" the same for others URLs.

The problem is, when I send a POST request to /my-class/ I get "POST request" and when I send a GET request to /my-class/creare I get "GET request"

I need the URL to work only for a specific request method. That is, url /my-class/create should only work for the POST method, url /my-class/update should only work for the PUT method and so on.

How can I do this? I've researched a lot, in the documentation and even right here but found no solution.

Insight answered 9/11, 2019 at 13:50 Comment(0)
M
12

You can pass http_method_names directly to the .as_view() method. Tested on Django==3.2.3

urlpatterns =[
    path('my-class/', views.MyClassView.as_view(http_method_names=['get'])),
    path('my-class/<int:id>/', views.MyClassView.as_view(http_method_names=['get'])),
    path('my-class/create/', views.MyClassView.as_view(http_method_names=['post'])),
    path('my-class/update/<int:id>/', views.MyClassView.as_view(http_method_names=['put'])),
    path('my-class/delete/<int:id>/', views.MyClassView.as_view(http_method_names=['delete'])),
]
Marsh answered 10/6, 2021 at 12:1 Comment(0)
I
5

I tried to develop a slightly lighter solution so you don't have to create a class for each request method.

Inspired by the operation of this feature in .NET Core I created a decotator to use in each method of the class. The code of this decorator is shown below:

from django.http import HttpResponse

def http_method_list(methods):
    def http_methods_decorator(func):
        def function_wrapper(self, request, **kwargs):
            methods = [method.upper() for method in methods]
            if not request.method.upper() in methods:
                return HttpResponse(status=405) # not allowed

            return func(self, request, **kwargs)
        return function_wrapper
    return http_methods_decorator

So in class we use:

class MyView(View):

    @http_method_list(["GET"])
    def get(self, request):
        return HttpResponse("Only GET requests")

    @http_method_list(["POST"])
    def post(self, request):
        return HttpResponse("Only POST requests")

    # and so on

Now get() method, for example, can be only executed by a GET request, the same for post() and the other methods in a class View.

I hope this can be useful for someone else.

Insight answered 13/11, 2019 at 1:56 Comment(2)
Seems like HttpResponse(status=405) is the same as HttpResponseNotAllowedCraft
You're correct. Btw, if you are facing a similar issue I recommend you to look into the accepted answer. Django has a native solution for that.Insight
S
3

You can limit the http methods using the http_method_names. But you should separate the view classes.

As an example:

from .views import PostView

urlpatterns = [
    path('only_post/', PostView.as_view(), name='post'),
]

and your view:

class PostView(View):
    http_method_names = ['post']

    def post(self, request):
        return HttpResponse('POST request')

Now you can only send a post request to this url, otherwise, you will get a HTTP ERROR 405 error.

Spraggins answered 9/11, 2019 at 14:11 Comment(2)
Yes. As far as I know, you can't use one view class for all of them (because there is no get_http_method_names method in the View class so that we can dynamically set this variable based on requested url).Spraggins
Thanks @Pedram You is correct! I was already aware of the solution, but I thought Django could offer me something more powerful.Insight

© 2022 - 2024 — McMap. All rights reserved.