This commit is contained in:
Tom Christie 2015-09-28 11:34:12 +00:00
commit ceda23ced6
13 changed files with 225 additions and 262 deletions

View File

@ -420,9 +420,6 @@ class BrowsableAPIRenderer(BaseRenderer):
if method not in view.allowed_methods:
return # Not a valid method
if not api_settings.FORM_METHOD_OVERRIDE:
return # Cannot use form overloading
try:
view.check_permissions(request)
if obj is not None:
@ -530,13 +527,6 @@ class BrowsableAPIRenderer(BaseRenderer):
instance = None
with override_method(view, request, method) as request:
# If we're not using content overloading there's no point in
# supplying a generic form, as the view won't treat the form's
# value as the content of the request.
if not (api_settings.FORM_CONTENT_OVERRIDE and
api_settings.FORM_CONTENTTYPE_OVERRIDE):
return None
# Check permissions
if not self.show_form_for_method(view, method, request, instance):
return
@ -564,28 +554,22 @@ class BrowsableAPIRenderer(BaseRenderer):
# Generate a generic form that includes a content type field,
# and a content field.
content_type_field = api_settings.FORM_CONTENTTYPE_OVERRIDE
content_field = api_settings.FORM_CONTENT_OVERRIDE
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]
# NB. http://jacobian.org/writing/dynamic-form-generation/
class GenericContentForm(forms.Form):
def __init__(self):
super(GenericContentForm, self).__init__()
self.fields[content_type_field] = forms.ChoiceField(
label='Media type',
choices=choices,
initial=initial
)
self.fields[content_field] = forms.CharField(
label='Content',
widget=forms.Textarea,
initial=content
)
_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
)
return GenericContentForm()

View File

@ -86,7 +86,7 @@ def clone_request(request, method):
ret._full_data = request._full_data
ret._content_type = request._content_type
ret._stream = request._stream
ret._method = method
ret.method = method
if hasattr(request, '_user'):
ret._user = request._user
if hasattr(request, '_auth'):
@ -129,11 +129,6 @@ class Request(object):
- authentication_classes(list/tuple). The authentications used to try
authenticating the request's user.
"""
_METHOD_PARAM = api_settings.FORM_METHOD_OVERRIDE
_CONTENT_PARAM = api_settings.FORM_CONTENT_OVERRIDE
_CONTENTTYPE_PARAM = api_settings.FORM_CONTENTTYPE_OVERRIDE
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
self._request = request
@ -144,7 +139,6 @@ class Request(object):
self._data = Empty
self._files = Empty
self._full_data = Empty
self._method = Empty
self._content_type = Empty
self._stream = Empty
@ -162,30 +156,10 @@ class Request(object):
def _default_negotiator(self):
return api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS()
@property
def method(self):
"""
Returns the HTTP method.
This allows the `method` to be overridden by using a hidden `form`
field on a form POST request.
"""
if not _hasattr(self, '_method'):
self._load_method_and_content_type()
return self._method
@property
def content_type(self):
"""
Returns the content type header.
This should be used instead of `request.META.get('HTTP_CONTENT_TYPE')`,
as it allows the content type to be overridden by using a hidden form
field on a form POST request.
"""
if not _hasattr(self, '_content_type'):
self._load_method_and_content_type()
return self._content_type
meta = self._request.META
return meta.get('CONTENT_TYPE', meta.get('HTTP_CONTENT_TYPE', ''))
@property
def stream(self):
@ -265,9 +239,6 @@ class Request(object):
"""
Parses the request content into `self.data`.
"""
if not _hasattr(self, '_content_type'):
self._load_method_and_content_type()
if not _hasattr(self, '_data'):
self._data, self._files = self._parse()
if self._files:
@ -276,32 +247,14 @@ class Request(object):
else:
self._full_data = self._data
def _load_method_and_content_type(self):
"""
Sets the method and content_type, and then check if they've
been overridden.
"""
self._content_type = self.META.get('HTTP_CONTENT_TYPE',
self.META.get('CONTENT_TYPE', ''))
self._perform_form_overloading()
if not _hasattr(self, '_method'):
self._method = self._request.method
# Allow X-HTTP-METHOD-OVERRIDE header
if 'HTTP_X_HTTP_METHOD_OVERRIDE' in self.META:
self._method = self.META['HTTP_X_HTTP_METHOD_OVERRIDE'].upper()
def _load_stream(self):
"""
Return the content body of the request, as a stream.
"""
meta = self._request.META
try:
content_length = int(
self.META.get(
'CONTENT_LENGTH', self.META.get('HTTP_CONTENT_LENGTH')
)
meta.get('CONTENT_LENGTH', meta.get('HTTP_CONTENT_LENGTH', 0))
)
except (ValueError, TypeError):
content_length = 0
@ -313,50 +266,6 @@ class Request(object):
else:
self._stream = six.BytesIO(self.raw_post_data)
def _perform_form_overloading(self):
"""
If this is a form POST request, then we need to check if the method and
content/content_type have been overridden by setting them in hidden
form fields or not.
"""
USE_FORM_OVERLOADING = (
self._METHOD_PARAM or
(self._CONTENT_PARAM and self._CONTENTTYPE_PARAM)
)
# We only need to use form overloading on form POST requests.
if (
self._request.method != 'POST' or
not USE_FORM_OVERLOADING or
not is_form_media_type(self._content_type)
):
return
# At this point we're committed to parsing the request as form data.
self._data = self._request.POST
self._files = self._request.FILES
self._full_data = self._data.copy()
self._full_data.update(self._files)
# Method overloading - change the method and remove the param from the content.
if (
self._METHOD_PARAM and
self._METHOD_PARAM in self._data
):
self._method = self._data[self._METHOD_PARAM].upper()
# Content overloading - modify the content type, and force re-parse.
if (
self._CONTENT_PARAM and
self._CONTENTTYPE_PARAM and
self._CONTENT_PARAM in self._data and
self._CONTENTTYPE_PARAM in self._data
):
self._content_type = self._data[self._CONTENTTYPE_PARAM]
self._stream = six.BytesIO(self._data[self._CONTENT_PARAM].encode(self.parser_context['encoding']))
self._data, self._files, self._full_data = (Empty, Empty, Empty)
def _parse(self):
"""
Parse the request content, returning a two-tuple of (data, files)

View File

@ -92,9 +92,6 @@ DEFAULTS = {
'TEST_REQUEST_DEFAULT_FORMAT': 'multipart',
# Browser enhancements
'FORM_METHOD_OVERRIDE': '_method',
'FORM_CONTENT_OVERRIDE': '_content',
'FORM_CONTENTTYPE_OVERRIDE': '_content_type',
'URL_ACCEPT_OVERRIDE': 'accept',
'URL_FORMAT_OVERRIDE': 'format',

View File

@ -0,0 +1,97 @@
function replaceDocument(docString) {
var doc = document.open("text/html");
doc.write(docString);
doc.close();
}
function doAjaxSubmit(e) {
var form = $(this);
var btn = $(this.clk);
var method = btn.data('method') || form.data('method') || form.attr('method') || 'GET';
method = method.toUpperCase()
if (method === 'GET') {
// GET requests can always use standard form submits.
return;
}
var contentType =
form.find('input[data-override="content-type"]').val() ||
form.find('select[data-override="content-type"] option:selected').text();
if (method === 'POST' && !contentType) {
// POST requests can use standard form submits, unless we have
// overridden the content type.
return;
}
// At this point we need to make an AJAX form submission.
e.preventDefault();
var url = form.attr('action');
var data;
if (contentType) {
data = form.find('[data-override="content"]').val() || ''
} else {
contentType = form.attr('enctype') || form.attr('encoding')
if (contentType === 'multipart/form-data') {
if (!window.FormData) {
alert('Your browser does not support AJAX multipart form submissions');
return;
}
// Use the FormData API and allow the content type to be set automatically,
// so it includes the boundary string.
// See https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects
contentType = false;
data = new FormData(form[0]);
} else {
contentType = 'application/x-www-form-urlencoded; charset=UTF-8'
data = form.serialize();
}
}
var ret = $.ajax({
url: url,
method: method,
data: data,
contentType: contentType,
processData: false,
headers: {'Accept': 'text/html; q=1.0, */*'},
});
ret.always(function(data, textStatus, jqXHR) {
if (textStatus != 'success') {
jqXHR = data;
}
var responseContentType = jqXHR.getResponseHeader("content-type") || "";
if (responseContentType.toLowerCase().indexOf('text/html') === 0) {
replaceDocument(jqXHR.responseText);
try {
// Modify the location and scroll to top, as if after page load.
history.replaceState({}, '', url);
scroll(0,0);
} catch(err) {
// History API not supported, so redirect.
window.location = url;
}
} else {
// Not HTML content. We can't open this directly, so redirect.
window.location = url;
}
});
return ret;
}
function captureSubmittingElement(e) {
var target = e.target;
var form = this;
form.clk = target;
}
$.fn.ajaxForm = function() {
var options = {}
return this
.unbind('submit.form-plugin click.form-plugin')
.bind('submit.form-plugin', options, doAjaxSubmit)
.bind('click.form-plugin', options, captureSubmittingElement);
};

View File

@ -0,0 +1,47 @@
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function sameOrigin(url) {
// test that a given url is a same-origin URL
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
var csrftoken = getCookie('csrftoken');
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
// Send the token to same-origin, relative URLs only.
// Send the token only if the method warrants CSRF protection
// Using the CSRFToken value acquired earlier
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -104,9 +104,7 @@
{% endif %}
{% if delete_form %}
<form class="button-form" action="{{ request.get_full_path }}" method="POST">
{% csrf_token %}
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="DELETE" />
<form class="button-form" action="{{ request.get_full_path }}" data-method="DELETE">
<button class="btn btn-danger">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Delete
</button>
@ -180,7 +178,7 @@
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">Edit</h4>
</div>
<form action="{{ request.get_full_path }}" method="POST" enctype="multipart/form-data" class="form-horizontal" novalidate>
<form action="{{ request.get_full_path }}" data-method="PUT" enctype="multipart/form-data" class="form-horizontal" novalidate>
<div class="modal-body">
<fieldset>
{{ put_form }}
@ -188,7 +186,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PUT" type="submit" class="btn btn-primary">Save</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
@ -204,7 +202,7 @@
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">{{ error_title }}</h4>
</div>
<form action="{{ request.get_full_path }}" method="POST" enctype="multipart/form-data" class="form-horizontal" novalidate>
<form action="{{ request.get_full_path }}" data-method="{{ request.method }}" enctype="multipart/form-data" class="form-horizontal" novalidate>
<div class="modal-body">
<fieldset>
{{ error_form }}
@ -212,7 +210,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="{{ request.method }}" type="submit" class="btn btn-primary">Save</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
@ -221,10 +219,17 @@
{% endif %}
{% block script %}
<script src="{% static "rest_framework/js/jquery-1.8.1-min.js" %}"></script>
<script src="{% static "rest_framework/js/jquery-1.11.3-min.js" %}"></script>
<script src="{% static "rest_framework/js/ajax-form.js" %}"></script>
<script src="{% static "rest_framework/js/csrf.js" %}"></script>
<script src="{% static "rest_framework/js/bootstrap.min.js" %}"></script>
<script src="{% static "rest_framework/js/prettify-min.js" %}"></script>
<script src="{% static "rest_framework/js/default.js" %}"></script>
<script>
$(document).ready(function() {
$('form').ajaxForm();
});
</script>
{% endblock %}
</body>
{% endblock %}

View File

@ -94,17 +94,13 @@
{% endif %}
{% if options_form %}
<form class="button-form" action="{{ request.get_full_path }}" method="POST">
{% csrf_token %}
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="OPTIONS" />
<form class="button-form" action="{{ request.get_full_path }}" data-method="OPTIONS">
<button class="btn btn-primary js-tooltip" title="Make an OPTIONS request on the {{ name }} resource">OPTIONS</button>
</form>
{% endif %}
{% if delete_form %}
<form class="button-form" action="{{ request.get_full_path }}" method="POST">
{% csrf_token %}
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="DELETE" />
<form class="button-form" action="{{ request.get_full_path }}" data-method="DELETE">
<button class="btn btn-danger js-tooltip" title="Make a DELETE request on the {{ name }} resource">DELETE</button>
</form>
{% endif %}
@ -168,7 +164,7 @@
</div>
{% endif %}
<div {% if post_form %}class="tab-pane"{% endif %} id="post-generic-content-form">
<div {% if raw_data_post_form %}class="tab-pane"{% endif %} id="post-generic-content-form">
{% with form=raw_data_post_form %}
<form action="{{ request.get_full_path }}" method="POST" class="form-horizontal">
<fieldset>
@ -200,11 +196,11 @@
<div class="well tab-content">
{% if put_form %}
<div class="tab-pane" id="put-object-form">
<form action="{{ request.get_full_path }}" method="POST" enctype="multipart/form-data" class="form-horizontal" novalidate>
<form action="{{ request.get_full_path }}" data-method="PUT" enctype="multipart/form-data" class="form-horizontal" novalidate>
<fieldset>
{{ put_form }}
<div class="form-actions">
<button class="btn btn-primary js-tooltip" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PUT" title="Make a PUT request on the {{ name }} resource">PUT</button>
<button class="btn btn-primary js-tooltip" title="Make a PUT request on the {{ name }} resource">PUT</button>
</div>
</fieldset>
</form>
@ -213,15 +209,15 @@
<div {% if put_form %}class="tab-pane"{% endif %} id="put-generic-content-form">
{% with form=raw_data_put_or_patch_form %}
<form action="{{ request.get_full_path }}" method="POST" class="form-horizontal">
<form action="{{ request.get_full_path }}" data-method="PUT" class="form-horizontal">
<fieldset>
{% include "rest_framework/raw_data_form.html" %}
<div class="form-actions">
{% if raw_data_put_form %}
<button class="btn btn-primary js-tooltip" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PUT" title="Make a PUT request on the {{ name }} resource">PUT</button>
<button class="btn btn-primary js-tooltip" title="Make a PUT request on the {{ name }} resource">PUT</button>
{% endif %}
{% if raw_data_patch_form %}
<button class="btn btn-primary js-tooltip" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PATCH" title="Make a PATCH request on the {{ name }} resource">PATCH</button>
<button data-method="PATCH" class="btn btn-primary js-tooltip" title="Make a PATCH request on the {{ name }} resource">PATCH</button>
{% endif %}
</div>
</fieldset>
@ -237,10 +233,17 @@
</div><!-- ./wrapper -->
{% block script %}
<script src="{% static "rest_framework/js/jquery-1.8.1-min.js" %}"></script>
<script src="{% static "rest_framework/js/jquery-1.11.3.min.js" %}"></script>
<script src="{% static "rest_framework/js/ajax-form.js" %}"></script>
<script src="{% static "rest_framework/js/csrf.js" %}"></script>
<script src="{% static "rest_framework/js/bootstrap.min.js" %}"></script>
<script src="{% static "rest_framework/js/prettify-min.js" %}"></script>
<script src="{% static "rest_framework/js/default.js" %}"></script>
<script>
$(document).ready(function() {
$('form').ajaxForm();
});
</script>
{% endblock %}
</body>
{% endblock %}

View File

@ -1,5 +1,4 @@
{% load rest_framework %}
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
<div class="form-group">

View File

@ -3,27 +3,20 @@ Tests for content parsing, and form-overloaded content parsing.
"""
from __future__ import unicode_literals
import json
from io import BytesIO
import django
import pytest
from django.conf.urls import url
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.contrib.sessions.middleware import SessionMiddleware
from django.core.handlers.wsgi import WSGIRequest
from django.test import TestCase
from django.utils import six
from rest_framework import status
from rest_framework.authentication import SessionAuthentication
from rest_framework.parsers import (
BaseParser, FormParser, JSONParser, MultiPartParser
)
from rest_framework.request import Empty, Request
from rest_framework.parsers import BaseParser, FormParser, MultiPartParser
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.test import APIClient, APIRequestFactory
from rest_framework.views import APIView
@ -43,36 +36,6 @@ class PlainTextParser(BaseParser):
return stream.read()
class TestMethodOverloading(TestCase):
def test_method(self):
"""
Request methods should be same as underlying request.
"""
request = Request(factory.get('/'))
self.assertEqual(request.method, 'GET')
request = Request(factory.post('/'))
self.assertEqual(request.method, 'POST')
def test_overloaded_method(self):
"""
POST requests can be overloaded to another method by setting a
reserved form field
"""
request = Request(factory.post('/', {api_settings.FORM_METHOD_OVERRIDE: 'DELETE'}))
self.assertEqual(request.method, 'DELETE')
def test_x_http_method_override_header(self):
"""
POST requests can also be overloaded to another method by setting
the X-HTTP-Method-Override header.
"""
request = Request(factory.post('/', {'foo': 'bar'}, HTTP_X_HTTP_METHOD_OVERRIDE='DELETE'))
self.assertEqual(request.method, 'DELETE')
request = Request(factory.get('/', {'foo': 'bar'}, HTTP_X_HTTP_METHOD_OVERRIDE='DELETE'))
self.assertEqual(request.method, 'DELETE')
class TestContentParsing(TestCase):
def test_standard_behaviour_determines_no_content_GET(self):
"""
@ -137,49 +100,6 @@ class TestContentParsing(TestCase):
request.parsers = (PlainTextParser(), )
self.assertEqual(request.data, content)
def test_overloaded_behaviour_allows_content_tunnelling(self):
"""
Ensure request.data returns content for overloaded POST request.
"""
json_data = {'foobar': 'qwerty'}
content = json.dumps(json_data)
content_type = 'application/json'
form_data = {
api_settings.FORM_CONTENT_OVERRIDE: content,
api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type
}
request = Request(factory.post('/', form_data))
request.parsers = (JSONParser(), )
self.assertEqual(request.data, json_data)
def test_form_POST_unicode(self):
"""
JSON POST via default web interface with unicode data
"""
# Note: environ and other variables here have simplified content compared to real Request
CONTENT = b'_content_type=application%2Fjson&_content=%7B%22request%22%3A+4%2C+%22firm%22%3A+1%2C+%22text%22%3A+%22%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%21%22%7D'
environ = {
'REQUEST_METHOD': 'POST',
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
'CONTENT_LENGTH': len(CONTENT),
'wsgi.input': BytesIO(CONTENT),
}
wsgi_request = WSGIRequest(environ=environ)
wsgi_request._load_post_and_files()
parsers = (JSONParser(), FormParser(), MultiPartParser())
parser_context = {
'encoding': 'utf-8',
'kwargs': {},
'args': (),
}
request = Request(wsgi_request, parsers=parsers, parser_context=parser_context)
method = request.method
self.assertEqual(method, 'POST')
self.assertEqual(request._content_type, 'application/json')
self.assertEqual(request._stream.getvalue(), b'{"request": 4, "firm": 1, "text": "\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82!"}')
self.assertEqual(request._data, Empty)
self.assertEqual(request._files, Empty)
class MockView(APIView):
authentication_classes = (SessionAuthentication,)

View File

@ -5,6 +5,7 @@ from django.test import TestCase
from django.utils import six
from rest_framework import generics, routers, serializers, status, viewsets
from rest_framework.parsers import JSONParser
from rest_framework.renderers import (
BaseRenderer, BrowsableAPIRenderer, JSONRenderer
)
@ -79,6 +80,14 @@ class MockViewSettingContentType(APIView):
return Response(DUMMYCONTENT, status=DUMMYSTATUS, content_type='setbyview')
class JSONView(APIView):
parser_classes = (JSONParser,)
def post(self, request, **kwargs):
assert request.data
return Response(DUMMYCONTENT)
class HTMLView(APIView):
renderer_classes = (BrowsableAPIRenderer, )
@ -114,6 +123,7 @@ urlpatterns = [
url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
url(r'^html$', HTMLView.as_view()),
url(r'^json$', JSONView.as_view()),
url(r'^html1$', HTMLView1.as_view()),
url(r'^html_new_model$', HTMLNewModelView.as_view()),
url(r'^html_new_model_viewset', include(new_model_viewset_router.urls)),
@ -203,6 +213,25 @@ class RendererIntegrationTests(TestCase):
self.assertEqual(resp.status_code, DUMMYSTATUS)
class UnsupportedMediaTypeTests(TestCase):
urls = 'tests.test_response'
def test_should_allow_posting_json(self):
response = self.client.post('/json', data='{"test": 123}', content_type='application/json')
self.assertEqual(response.status_code, 200)
def test_should_not_allow_posting_xml(self):
response = self.client.post('/json', data='<test>123</test>', content_type='application/xml')
self.assertEqual(response.status_code, 415)
def test_should_not_allow_posting_a_form(self):
response = self.client.post('/json', data={'test': 123})
self.assertEqual(response.status_code, 415)
class Issue122Tests(TestCase):
"""
Tests that covers #122.

View File

@ -74,21 +74,6 @@ class ClassBasedViewIntegrationTests(TestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(sanitise_json_error(response.data), expected)
def test_400_parse_error_tunneled_content(self):
content = 'f00bar'
content_type = 'application/json'
form_data = {
api_settings.FORM_CONTENT_OVERRIDE: content,
api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type
}
request = factory.post('/', form_data)
response = self.view(request)
expected = {
'detail': JSON_ERROR
}
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(sanitise_json_error(response.data), expected)
class FunctionBasedViewIntegrationTests(TestCase):
def setUp(self):
@ -103,21 +88,6 @@ class FunctionBasedViewIntegrationTests(TestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(sanitise_json_error(response.data), expected)
def test_400_parse_error_tunneled_content(self):
content = 'f00bar'
content_type = 'application/json'
form_data = {
api_settings.FORM_CONTENT_OVERRIDE: content,
api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type
}
request = factory.post('/', form_data)
response = self.view(request)
expected = {
'detail': JSON_ERROR
}
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(sanitise_json_error(response.data), expected)
class TestCustomExceptionHandler(TestCase):
def setUp(self):