mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-27 08:29:59 +03:00
Merge e53b66bed8
into 3bcd38b7d0
This commit is contained in:
commit
d710ff9cec
|
@ -427,6 +427,7 @@ class BrowsableAPIRenderer(BaseRenderer):
|
||||||
content = self.get_content(renderer, data, accepted_media_type, renderer_context)
|
content = self.get_content(renderer, data, accepted_media_type, renderer_context)
|
||||||
|
|
||||||
put_form = self.get_form(view, 'PUT', request)
|
put_form = self.get_form(view, 'PUT', request)
|
||||||
|
patch_form = self.get_form(view, 'PATCH', request)
|
||||||
post_form = self.get_form(view, 'POST', request)
|
post_form = self.get_form(view, 'POST', request)
|
||||||
delete_form = self.get_form(view, 'DELETE', request)
|
delete_form = self.get_form(view, 'DELETE', request)
|
||||||
options_form = self.get_form(view, 'OPTIONS', request)
|
options_form = self.get_form(view, 'OPTIONS', request)
|
||||||
|
@ -448,6 +449,7 @@ class BrowsableAPIRenderer(BaseRenderer):
|
||||||
'allowed_methods': view.allowed_methods,
|
'allowed_methods': view.allowed_methods,
|
||||||
'available_formats': [renderer.format for renderer in view.renderer_classes],
|
'available_formats': [renderer.format for renderer in view.renderer_classes],
|
||||||
'put_form': put_form,
|
'put_form': put_form,
|
||||||
|
'patch_form': patch_form,
|
||||||
'post_form': post_form,
|
'post_form': post_form,
|
||||||
'delete_form': delete_form,
|
'delete_form': delete_form,
|
||||||
'options_form': options_form,
|
'options_form': options_form,
|
||||||
|
|
|
@ -3,3 +3,11 @@ prettyPrint();
|
||||||
$('.js-tooltip').tooltip({
|
$('.js-tooltip').tooltip({
|
||||||
delay: 1000
|
delay: 1000
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#patch-form').find('.field-switcher').on('change', function() {
|
||||||
|
var $this = $(this);
|
||||||
|
$('#patch-form').find('#'+$this.attr('data-field-id'))
|
||||||
|
.prop('disabled', !$this.prop('checked'));
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#form-method-switcher a:first').tab('show');
|
||||||
|
|
|
@ -147,32 +147,64 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if put_form %}
|
{% if put_form or patch_form %}
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<form action="{{ request.get_full_path }}" method="POST" {% if put_form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
|
<ul class="nav nav-pills" id="form-method-switcher">
|
||||||
<fieldset>
|
{% if put_form %}<li><a href="#put-form" data-toggle="pill">PUT Form</a></li>{% endif %}
|
||||||
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PUT" />
|
{% if patch_form %}<li><a href="#patch-form" data-toggle="pill">PATCH Form</a></li>{% endif %}
|
||||||
{% csrf_token %}
|
</ul>
|
||||||
{{ put_form.non_field_errors }}
|
<div class="tab-content">
|
||||||
{% for field in put_form %}
|
{% if put_form %}
|
||||||
<div class="control-group"> <!--{% if field.errors %}error{% endif %}-->
|
<div class="tab-pane" id="put-form">
|
||||||
{{ field.label_tag|add_class:"control-label" }}
|
<form action="{{ request.get_full_path }}" method="POST" {% if put_form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
|
||||||
<div class="controls">
|
<fieldset>
|
||||||
{{ field }}
|
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PUT" />
|
||||||
<span class='help-inline'>{{ field.help_text }}</span>
|
{% csrf_token %}
|
||||||
<!--{{ field.errors|add_class:"help-block" }}-->
|
{{ put_form.non_field_errors }}
|
||||||
|
{% for field in put_form %}
|
||||||
|
<div class="control-group"> <!--{% if field.errors %}error{% endif %}-->
|
||||||
|
{{ field.label_tag|add_class:"control-label" }}
|
||||||
|
<div class="controls">
|
||||||
|
{{ field }}
|
||||||
|
<span class='help-inline'>{{ field.help_text }}</span>
|
||||||
|
<!--{{ field.errors|add_class:"help-block" }}-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="form-actions">
|
||||||
|
<button class="btn btn-primary js-tooltip" title="Make a PUT request on the {{ name }} resource">PUT</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
{% endfor %}
|
</form>
|
||||||
<div class="form-actions">
|
</div>
|
||||||
<button class="btn btn-primary js-tooltip" title="Make a PUT request on the {{ name }} resource">PUT</button>
|
{% endif %}
|
||||||
</div>
|
{% if patch_form %}
|
||||||
|
<div class="tab-pane" id="patch-form">
|
||||||
</fieldset>
|
<form action="{{ request.get_full_path }}" method="POST" {% if patch_form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
|
||||||
</form>
|
<fieldset>
|
||||||
|
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PATCH" />
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ patch_form.non_field_errors }}
|
||||||
|
{% for field in patch_form %}
|
||||||
|
<div class="control-group"> <!--{% if field.errors %}error{% endif %}-->
|
||||||
|
{{ field.label_tag|add_class:"control-label" }}
|
||||||
|
<div class="controls">
|
||||||
|
{{ field }} <input checked="checked" type="checkbox" id="{{ field.html_name }}_field_sent" class="field-switcher" data-field-id="{{ field.auto_id }}">
|
||||||
|
<span class='help-inline'>{{ field.help_text }}</span>
|
||||||
|
<!--{{ field.errors|add_class:"help-block" }}-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="form-actions">
|
||||||
|
<button class="btn btn-primary js-tooltip" title="Make a PATCH request on the {{ name }} resource">PATCH</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.test import Client, TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from rest_framework import permissions
|
from rest_framework import permissions
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.authentication import TokenAuthentication, BasicAuthentication, SessionAuthentication
|
from rest_framework.authentication import TokenAuthentication, BasicAuthentication, SessionAuthentication
|
||||||
from rest_framework.compat import patterns
|
from rest_framework.compat import patterns
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.tests.utils import Client
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
|
@ -18,6 +19,9 @@ class MockView(APIView):
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
return HttpResponse({'a': 1, 'b': 2, 'c': 3})
|
return HttpResponse({'a': 1, 'b': 2, 'c': 3})
|
||||||
|
|
||||||
|
def patch(self, request):
|
||||||
|
return HttpResponse({'a': 1, 'b': 2, 'c': 3})
|
||||||
|
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
return HttpResponse({'a': 1, 'b': 2, 'c': 3})
|
return HttpResponse({'a': 1, 'b': 2, 'c': 3})
|
||||||
|
|
||||||
|
@ -103,6 +107,14 @@ class SessionAuthTests(TestCase):
|
||||||
response = self.non_csrf_client.put('/session/', {'example': 'example'})
|
response = self.non_csrf_client.put('/session/', {'example': 'example'})
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_patch_form_session_auth_passing(self):
|
||||||
|
"""
|
||||||
|
Ensure PATCHting form over session authentication with logged in user and CSRF token passes.
|
||||||
|
"""
|
||||||
|
self.non_csrf_client.login(username=self.username, password=self.password)
|
||||||
|
response = self.non_csrf_client.patch('/', {'example': 'example'})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def test_post_form_session_auth_failing(self):
|
def test_post_form_session_auth_failing(self):
|
||||||
"""
|
"""
|
||||||
Ensure POSTing form over session authentication without logged in user fails.
|
Ensure POSTing form over session authentication without logged in user fails.
|
||||||
|
|
|
@ -111,6 +111,9 @@ class POSTDeniedView(APIView):
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
|
def patch(self, request):
|
||||||
|
return Response()
|
||||||
|
|
||||||
|
|
||||||
class DocumentingRendererTests(TestCase):
|
class DocumentingRendererTests(TestCase):
|
||||||
def test_only_permitted_forms_are_displayed(self):
|
def test_only_permitted_forms_are_displayed(self):
|
||||||
|
@ -119,6 +122,7 @@ class DocumentingRendererTests(TestCase):
|
||||||
response = view(request).render()
|
response = view(request).render()
|
||||||
self.assertNotContains(response, '>POST<')
|
self.assertNotContains(response, '>POST<')
|
||||||
self.assertContains(response, '>PUT<')
|
self.assertContains(response, '>PUT<')
|
||||||
|
self.assertContains(response, '>PATCH<')
|
||||||
|
|
||||||
|
|
||||||
class RendererEndToEndTests(TestCase):
|
class RendererEndToEndTests(TestCase):
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
from django.test.client import RequestFactory, FakePayload
|
from django.test.client import Client as _Client, RequestFactory as _RequestFactory, FakePayload
|
||||||
from django.test.client import MULTIPART_CONTENT
|
from django.test.client import MULTIPART_CONTENT
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
|
||||||
class RequestFactory(RequestFactory):
|
class RequestFactory(_RequestFactory):
|
||||||
|
|
||||||
def __init__(self, **defaults):
|
def __init__(self, **defaults):
|
||||||
super(RequestFactory, self).__init__(**defaults)
|
super(RequestFactory, self).__init__(**defaults)
|
||||||
|
@ -25,3 +25,15 @@ class RequestFactory(RequestFactory):
|
||||||
}
|
}
|
||||||
r.update(extra)
|
r.update(extra)
|
||||||
return self.request(**r)
|
return self.request(**r)
|
||||||
|
|
||||||
|
|
||||||
|
class Client(_Client, RequestFactory):
|
||||||
|
def patch(self, path, data={}, content_type=MULTIPART_CONTENT,
|
||||||
|
follow=False, **extra):
|
||||||
|
"""
|
||||||
|
Send a resource to the server using PATCH.
|
||||||
|
"""
|
||||||
|
response = super(Client, self).patch(path, data=data, content_type=content_type, **extra)
|
||||||
|
if follow:
|
||||||
|
response = self._handle_redirects(response, **extra)
|
||||||
|
return response
|
||||||
|
|
Loading…
Reference in New Issue
Block a user