mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-30 23:47:53 +03:00 
			
		
		
		
	Remove method and content overriding
This commit is contained in:
		
							parent
							
								
									a67eed1466
								
							
						
					
					
						commit
						566812ac0b
					
				|  | @ -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() | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -162,30 +157,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 +240,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 +248,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 +267,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) | ||||
|  |  | |||
|  | @ -94,9 +94,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', | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										97
									
								
								rest_framework/static/rest_framework/js/ajax-form.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								rest_framework/static/rest_framework/js/ajax-form.js
									
									
									
									
									
										Normal 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); | ||||
| }; | ||||
							
								
								
									
										47
									
								
								rest_framework/static/rest_framework/js/csrf.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								rest_framework/static/rest_framework/js/csrf.js
									
									
									
									
									
										Normal 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); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
							
								
								
									
										5
									
								
								rest_framework/static/rest_framework/js/jquery-1.11.3.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								rest_framework/static/rest_framework/js/jquery-1.11.3.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -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 %} | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| {% load rest_framework %} | ||||
| {% csrf_token %} | ||||
| {{ form.non_field_errors }} | ||||
| {% for field in form %} | ||||
|   <div class="form-group"> | ||||
|  |  | |||
|  | @ -43,36 +43,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 +107,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,) | ||||
|  |  | |||
|  | @ -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): | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user