mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-31 16:07:38 +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: |         if method not in view.allowed_methods: | ||||||
|             return  # Not a valid method |             return  # Not a valid method | ||||||
| 
 | 
 | ||||||
|         if not api_settings.FORM_METHOD_OVERRIDE: |  | ||||||
|             return  # Cannot use form overloading |  | ||||||
| 
 |  | ||||||
|         try: |         try: | ||||||
|             view.check_permissions(request) |             view.check_permissions(request) | ||||||
|             if obj is not None: |             if obj is not None: | ||||||
|  | @ -530,13 +527,6 @@ class BrowsableAPIRenderer(BaseRenderer): | ||||||
|             instance = None |             instance = None | ||||||
| 
 | 
 | ||||||
|         with override_method(view, request, method) as request: |         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 |             # Check permissions | ||||||
|             if not self.show_form_for_method(view, method, request, instance): |             if not self.show_form_for_method(view, method, request, instance): | ||||||
|                 return |                 return | ||||||
|  | @ -564,28 +554,22 @@ class BrowsableAPIRenderer(BaseRenderer): | ||||||
| 
 | 
 | ||||||
|             # Generate a generic form that includes a content type field, |             # Generate a generic form that includes a content type field, | ||||||
|             # and a content 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] |             media_types = [parser.media_type for parser in view.parser_classes] | ||||||
|             choices = [(media_type, media_type) for media_type in media_types] |             choices = [(media_type, media_type) for media_type in media_types] | ||||||
|             initial = media_types[0] |             initial = media_types[0] | ||||||
| 
 | 
 | ||||||
|             # NB. http://jacobian.org/writing/dynamic-form-generation/ |  | ||||||
|             class GenericContentForm(forms.Form): |             class GenericContentForm(forms.Form): | ||||||
|                 def __init__(self): |                 _content_type = forms.ChoiceField( | ||||||
|                     super(GenericContentForm, self).__init__() |                     label='Media type', | ||||||
| 
 |                     choices=choices, | ||||||
|                     self.fields[content_type_field] = forms.ChoiceField( |                     initial=initial, | ||||||
|                         label='Media type', |                     widget=forms.Select(attrs={'data-override': 'content-type'}) | ||||||
|                         choices=choices, |                 ) | ||||||
|                         initial=initial |                 _content = forms.CharField( | ||||||
|                     ) |                     label='Content', | ||||||
|                     self.fields[content_field] = forms.CharField( |                     widget=forms.Textarea(attrs={'data-override': 'content'}), | ||||||
|                         label='Content', |                     initial=content | ||||||
|                         widget=forms.Textarea, |                 ) | ||||||
|                         initial=content |  | ||||||
|                     ) |  | ||||||
| 
 | 
 | ||||||
|             return GenericContentForm() |             return GenericContentForm() | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -129,11 +129,6 @@ class Request(object): | ||||||
|         - authentication_classes(list/tuple). The authentications used to try |         - authentication_classes(list/tuple). The authentications used to try | ||||||
|           authenticating the request's user. |           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, |     def __init__(self, request, parsers=None, authenticators=None, | ||||||
|                  negotiator=None, parser_context=None): |                  negotiator=None, parser_context=None): | ||||||
|         self._request = request |         self._request = request | ||||||
|  | @ -162,30 +157,10 @@ class Request(object): | ||||||
|     def _default_negotiator(self): |     def _default_negotiator(self): | ||||||
|         return api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS() |         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 |     @property | ||||||
|     def content_type(self): |     def content_type(self): | ||||||
|         """ |         meta = self._request.META | ||||||
|         Returns the content type header. |         return meta.get('CONTENT_TYPE', meta.get('HTTP_CONTENT_TYPE', '')) | ||||||
| 
 |  | ||||||
|         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 |  | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def stream(self): |     def stream(self): | ||||||
|  | @ -265,9 +240,6 @@ class Request(object): | ||||||
|         """ |         """ | ||||||
|         Parses the request content into `self.data`. |         Parses the request content into `self.data`. | ||||||
|         """ |         """ | ||||||
|         if not _hasattr(self, '_content_type'): |  | ||||||
|             self._load_method_and_content_type() |  | ||||||
| 
 |  | ||||||
|         if not _hasattr(self, '_data'): |         if not _hasattr(self, '_data'): | ||||||
|             self._data, self._files = self._parse() |             self._data, self._files = self._parse() | ||||||
|             if self._files: |             if self._files: | ||||||
|  | @ -276,32 +248,14 @@ class Request(object): | ||||||
|             else: |             else: | ||||||
|                 self._full_data = self._data |                 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): |     def _load_stream(self): | ||||||
|         """ |         """ | ||||||
|         Return the content body of the request, as a stream. |         Return the content body of the request, as a stream. | ||||||
|         """ |         """ | ||||||
|  |         meta = self._request.META | ||||||
|         try: |         try: | ||||||
|             content_length = int( |             content_length = int( | ||||||
|                 self.META.get( |                 meta.get('CONTENT_LENGTH', meta.get('HTTP_CONTENT_LENGTH', 0)) | ||||||
|                     'CONTENT_LENGTH', self.META.get('HTTP_CONTENT_LENGTH') |  | ||||||
|                 ) |  | ||||||
|             ) |             ) | ||||||
|         except (ValueError, TypeError): |         except (ValueError, TypeError): | ||||||
|             content_length = 0 |             content_length = 0 | ||||||
|  | @ -313,50 +267,6 @@ class Request(object): | ||||||
|         else: |         else: | ||||||
|             self._stream = six.BytesIO(self.raw_post_data) |             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): |     def _parse(self): | ||||||
|         """ |         """ | ||||||
|         Parse the request content, returning a two-tuple of (data, files) |         Parse the request content, returning a two-tuple of (data, files) | ||||||
|  |  | ||||||
|  | @ -94,9 +94,6 @@ DEFAULTS = { | ||||||
|     'TEST_REQUEST_DEFAULT_FORMAT': 'multipart', |     'TEST_REQUEST_DEFAULT_FORMAT': 'multipart', | ||||||
| 
 | 
 | ||||||
|     # Browser enhancements |     # Browser enhancements | ||||||
|     'FORM_METHOD_OVERRIDE': '_method', |  | ||||||
|     'FORM_CONTENT_OVERRIDE': '_content', |  | ||||||
|     'FORM_CONTENTTYPE_OVERRIDE': '_content_type', |  | ||||||
|     'URL_ACCEPT_OVERRIDE': 'accept', |     'URL_ACCEPT_OVERRIDE': 'accept', | ||||||
|     'URL_FORMAT_OVERRIDE': 'format', |     '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 %} |         {% endif %} | ||||||
| 
 | 
 | ||||||
|         {% if options_form %} |         {% if options_form %} | ||||||
|           <form class="button-form" action="{{ request.get_full_path }}" method="POST"> |           <form class="button-form" action="{{ request.get_full_path }}" data-method="OPTIONS"> | ||||||
|             {% csrf_token %} |  | ||||||
|             <input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="OPTIONS" /> |  | ||||||
|             <button class="btn btn-primary js-tooltip" title="Make an OPTIONS request on the {{ name }} resource">OPTIONS</button> |             <button class="btn btn-primary js-tooltip" title="Make an OPTIONS request on the {{ name }} resource">OPTIONS</button> | ||||||
|           </form> |           </form> | ||||||
|         {% endif %} |         {% endif %} | ||||||
| 
 | 
 | ||||||
|         {% if delete_form %} |         {% if delete_form %} | ||||||
|           <form class="button-form" action="{{ request.get_full_path }}" method="POST"> |           <form class="button-form" action="{{ request.get_full_path }}" data-method="DELETE"> | ||||||
|             {% csrf_token %} |  | ||||||
|             <input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="DELETE" /> |  | ||||||
|             <button class="btn btn-danger js-tooltip" title="Make a DELETE request on the {{ name }} resource">DELETE</button> |             <button class="btn btn-danger js-tooltip" title="Make a DELETE request on the {{ name }} resource">DELETE</button> | ||||||
|           </form> |           </form> | ||||||
|         {% endif %} |         {% endif %} | ||||||
|  | @ -168,7 +164,7 @@ | ||||||
|                     </div> |                     </div> | ||||||
|                   {% endif %} |                   {% 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 %} |                     {% with form=raw_data_post_form %} | ||||||
|                       <form action="{{ request.get_full_path }}" method="POST" class="form-horizontal"> |                       <form action="{{ request.get_full_path }}" method="POST" class="form-horizontal"> | ||||||
|                         <fieldset> |                         <fieldset> | ||||||
|  | @ -200,11 +196,11 @@ | ||||||
|                 <div class="well tab-content"> |                 <div class="well tab-content"> | ||||||
|                   {% if put_form %} |                   {% if put_form %} | ||||||
|                     <div class="tab-pane" id="put-object-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> |                         <fieldset> | ||||||
|                           {{ put_form }} |                           {{ put_form }} | ||||||
|                           <div class="form-actions"> |                           <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> |                           </div> | ||||||
|                         </fieldset> |                         </fieldset> | ||||||
|                       </form> |                       </form> | ||||||
|  | @ -213,15 +209,15 @@ | ||||||
| 
 | 
 | ||||||
|                   <div {% if put_form %}class="tab-pane"{% endif %} id="put-generic-content-form"> |                   <div {% if put_form %}class="tab-pane"{% endif %} id="put-generic-content-form"> | ||||||
|                     {% with form=raw_data_put_or_patch_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> |                         <fieldset> | ||||||
|                           {% include "rest_framework/raw_data_form.html" %} |                           {% include "rest_framework/raw_data_form.html" %} | ||||||
|                           <div class="form-actions"> |                           <div class="form-actions"> | ||||||
|                             {% if raw_data_put_form %} |                             {% 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 %} |                             {% endif %} | ||||||
|                             {% if raw_data_patch_form %} |                             {% 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 %} |                               {% endif %} | ||||||
|                           </div> |                           </div> | ||||||
|                         </fieldset> |                         </fieldset> | ||||||
|  | @ -237,10 +233,17 @@ | ||||||
|   </div><!-- ./wrapper --> |   </div><!-- ./wrapper --> | ||||||
| 
 | 
 | ||||||
|   {% block script %} |   {% 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/bootstrap.min.js" %}"></script> | ||||||
|     <script src="{% static "rest_framework/js/prettify-min.js" %}"></script> |     <script src="{% static "rest_framework/js/prettify-min.js" %}"></script> | ||||||
|     <script src="{% static "rest_framework/js/default.js" %}"></script> |     <script src="{% static "rest_framework/js/default.js" %}"></script> | ||||||
|  |     <script> | ||||||
|  |         $(document).ready(function() { | ||||||
|  |             $('form').ajaxForm(); | ||||||
|  |         }); | ||||||
|  |     </script> | ||||||
|   {% endblock %} |   {% endblock %} | ||||||
| </body> | </body> | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| {% load rest_framework %} | {% load rest_framework %} | ||||||
| {% csrf_token %} |  | ||||||
| {{ form.non_field_errors }} | {{ form.non_field_errors }} | ||||||
| {% for field in form %} | {% for field in form %} | ||||||
|   <div class="form-group"> |   <div class="form-group"> | ||||||
|  |  | ||||||
|  | @ -43,36 +43,6 @@ class PlainTextParser(BaseParser): | ||||||
|         return stream.read() |         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): | class TestContentParsing(TestCase): | ||||||
|     def test_standard_behaviour_determines_no_content_GET(self): |     def test_standard_behaviour_determines_no_content_GET(self): | ||||||
|         """ |         """ | ||||||
|  | @ -137,49 +107,6 @@ class TestContentParsing(TestCase): | ||||||
|         request.parsers = (PlainTextParser(), ) |         request.parsers = (PlainTextParser(), ) | ||||||
|         self.assertEqual(request.data, content) |         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): | class MockView(APIView): | ||||||
|     authentication_classes = (SessionAuthentication,) |     authentication_classes = (SessionAuthentication,) | ||||||
|  |  | ||||||
|  | @ -74,21 +74,6 @@ class ClassBasedViewIntegrationTests(TestCase): | ||||||
|         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) |         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||||||
|         self.assertEqual(sanitise_json_error(response.data), expected) |         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): | class FunctionBasedViewIntegrationTests(TestCase): | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|  | @ -103,21 +88,6 @@ class FunctionBasedViewIntegrationTests(TestCase): | ||||||
|         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) |         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||||||
|         self.assertEqual(sanitise_json_error(response.data), expected) |         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): | class TestCustomExceptionHandler(TestCase): | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user