Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override the request method (from 'PUT' into 'POST') in Django Rest Framework

I'm coding a plugin-wrapper for provide a app to restfull, problem is the app don't process PUT request, then I get from resfull a PUT request and should change request's method into a POST request.

I have tried this:

self.context['request'].method = 'POST'

but obviously don't works

How I could do it? Thanks

Update:

Serializer code:

class PageEditSerializer(serializers.ModelSerializer):
    """
    Serializer Class to edit and delete a Page.
    """
    raw = serializers.CharField()

    def save(self):

        #no works
        self.context['request'].method = 'POST'

        """call to waliki new function"""
        views.edit(self.context['request'], self.instance.slug)

    class Meta():
        model = Page
        fields = ('id', 'title', 'slug', 'raw', )
        read_only_fields = ('id', 'slug', )

View code:

class PageEditView(generics.RetrieveUpdateAPIView):
    """
    A simple View to edit a Page.
    """
    lookup_field = 'slug'
    serializer_class = PageEditSerializer
    permission_classes = (permissions.AllowAny, )

    def get_queryset(self):
        return Page.objects.filter(slug=self.kwargs['slug'])
like image 320
aslheyrr Avatar asked Jan 07 '23 23:01

aslheyrr


2 Answers

I will divide my answer into 3 parts.

PART-1: Does Django Rest Framework (DRF) explicitly supports request method overriding?

Yes, DRF has provided 2 ways in which the request method can be overridden.

  1. Using a hidden form field:

DRF supports browser-based PUT, DELETE and other methods, by overloading POST requests using a hidden form field.

For example:

<form action="/some/url" method="POST">
    <input type="hidden" name="_method" value="PUT">
</form>

In the above example, we have overloaded the POST method with PUT by using the hidden form field with name as _method.

request.method will now return PUT instead of POST.

  1. HTTP header based method overriding:

DRF also supports method overriding via the X-HTTP-Method-Override header. Just set the X-HTTP-Method-Override header with the value of the desired method.

For example, making a PATCH request via POST in jQuery:

$.ajax({
    url: '/myresource/',
    method: 'POST',
    headers: {'X-HTTP-Method-Override': 'PATCH'},
    ...
});

This approach is useful when working with non-form content such as JSON or when working on an older web server and/or hosting provider that doesn't recognise particular HTTP methods such as PATCH.

PART-2: Why your code is not working?

Request method is stored in a variable _method and the property method is used to access the value of it.

Your way of setting value of request method would have worked had there been a setter defined for the request's _method attribute. Since it is not defined, you can't set the value of request method in the way you are doing.

PART-3: How to override the request method in your case?

Solution-1(Quick): Directly set the _method attribute

You can directly set the _method to your desired value which will override the request method.

class PageEditSerializer(serializers.ModelSerializer):
    """
    Serializer Class to edit and delete a Page.
    """
    raw = serializers.CharField()

    def save(self):

        self.context['request']._method = 'POST' # override the request method

        """call to waliki new function"""
        views.edit(self.context['request'], self.instance.slug)

    class Meta():
        model = Page
        fields = ('id', 'title', 'slug', 'raw', )
        read_only_fields = ('id', 'slug', )

This will override the request method to POST.

Solution-2: Set X-HTTP-Method-Override header in request via middleware

When initialize_request() is called in DRF view, DRF checks for X-HTTP-Method-Override header in the Django request. If it is set, it overrides the current request method to the method defined in that header.

So before DRF initializes and prepares a DRF request, we can set the X-HTTP-Method-Override header to our desired method which will override the request method.

We create a PutRequestMethodOverrideMiddleware class and set the X-HTTP-Method-Override header in the request in case the request is PUT and this particular header is not already set.

class PutRequestMethodOverrideMiddleware(object):  

    def process_request(self, request):  
        override_method = request.META.get('X-HTTP-Method-Override')
        if request.method=='PUT' and not override_method: 
            # Override method in case of PUT requests and header not already set
            request.META['X-HTTP-Method-Override'] = 'POST' # set to desired method value
        return

Then in your settings, add this middleware to your MIDDLEWARE_CLASSES.

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    ...
    ...
    # your custom middleware 
    'my_project.middlewares.PutRequestMethodOverrideMiddleware',
)

But remember, this method will override all the PUT requests method to POST method.

Here, PutRequestMethodOverrideMiddleware will be executed at the end. You can put it at the position you want it to execute.

like image 69
Rahul Gupta Avatar answered Jan 10 '23 11:01

Rahul Gupta


As of version 3.3, DRF has dropped support for the method overloading mechanisms via both _method form field and X-HTTP-Method-Override header. You need to implement it yourself for the latest version.

The document provides an example by adding a custom middleware:

METHOD_OVERRIDE_HEADER = 'HTTP_X_HTTP_METHOD_OVERRIDE'

class MethodOverrideMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if request.method == 'POST' and METHOD_OVERRIDE_HEADER in request.META:
            request.method = request.META[METHOD_OVERRIDE_HEADER]
        return self.get_response(request)

Another solution is to override dispatch method in each of your APIView:

class MyView(APIView):

    def dispatch(self, request, *args, **kwargs):
        if request.method == 'POST' and METHOD_OVERRIDE_HEADER in request.META:
            request.method = request.META[METHOD_OVERRIDE_HEADER]
        return super().dispatch(request, *args, **kwargs)
like image 24
okapies Avatar answered Jan 10 '23 11:01

okapies