Understanding browser method overloading and format handling in Django REST Framework
There are two noncontroversial uses for overloaded POST. The first is to simulate HTTP’s uniform interface for clients like web browsers that don’t support PUT or DELETE.— RESTful Web Services, Leonard Richardson & Sam Ruby
REST framework provides several browser enhancements that allow the browsable API to function smoothly, even with browser limitations.
Breaking Change: Prior to version 3.3.0, method overloading was server-side using the Ruby on Rails style. This is no longer supported due to subtle issues in request parsing. Always use the JavaScript-based approach with data-method.
<form action="/api/articles/42/" data-method="DELETE"> <p>Are you sure you want to delete this article?</p> <button type="submit" class="btn btn-danger">Delete</button> <a href="/api/articles/42/" class="btn btn-default">Cancel</a></form>
REST framework supports ?format= URL parameters for content negotiation:
# Get JSON responsehttp://api.example.com/users/?format=json# Get browsable API (HTML)http://api.example.com/users/?format=api# Get XML response (if XMLRenderer is configured)http://api.example.com/users/?format=xml
Removed in 3.3.0: The semi-standard X-HTTP-Method-Override header is no longer supported in core. Add custom middleware if you need this functionality.
If you need to support the X-HTTP-Method-Override header for legacy clients:
# middleware.pyMETHOD_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)
The browsable API uses these enhancements automatically. When you interact with forms in the browsable API:View with GET:Edit with PUT/PATCH:Delete with DELETE:
The browsable API provides raw data forms for submitting JSON, XML, or other formats:
rest_framework/renderers.py
def get_raw_data_form(self, data, view, method, request): """ Returns a form that allows for arbitrary content types to be tunneled via standard HTML forms. """ media_types = [parser.media_type for parser in view.parser_classes] choices = [(media_type, media_type) for media_type in media_types] initial = media_types[0] class GenericContentForm(forms.Form): _content_type = forms.ChoiceField( label='Media type', choices=choices, initial=initial, widget=forms.Select(attrs={'data-override': 'content-type'}) ) _content = forms.CharField( label='Content', widget=forms.Textarea(attrs={'data-override': 'content'}), initial=content, required=False ) return GenericContentForm()
Always use the data-method attribute with the ajax-form library for method overriding. Don’t implement server-side method overriding as it can cause parsing issues.
<form data-method="PUT"> <!-- form fields --></form>
Prefer URL Format Parameter
Use ?format=json for format negotiation in browser testing:
http://api.example.com/users/?format=json
Use Proper HTTP Methods in Non-Browser Clients
When building API clients (mobile apps, SPAs), use native HTTP methods rather than overrides: