aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework
diff options
context:
space:
mode:
authorTom Christie2013-02-20 12:18:57 +0000
committerTom Christie2013-02-20 12:18:57 +0000
commit03afaee423967e09fe266e9fe124f661c1e10ccb (patch)
tree22cb4f529d34cdd60ec860ef73255d0293d14d00 /rest_framework
parent47a4f0863d08e4b839ea3bbd7308ecc0f995b7d9 (diff)
parent2fb6fa2dd3b336cc442e707dbb80a4d5616582a6 (diff)
downloaddjango-rest-framework-03afaee423967e09fe266e9fe124f661c1e10ccb.tar.bz2
Merge branch 'browsable_api_patch' of https://github.com/wronglink/django-rest-framework into generic-form-input
Diffstat (limited to 'rest_framework')
-rw-r--r--rest_framework/renderers.py10
-rw-r--r--rest_framework/static/rest_framework/css/default.css11
-rw-r--r--rest_framework/static/rest_framework/js/default.js2
-rw-r--r--rest_framework/templates/rest_framework/base.html116
-rw-r--r--rest_framework/templates/rest_framework/form.html13
-rw-r--r--rest_framework/tests/renderers.py4
-rw-r--r--rest_framework/tests/utils.py16
7 files changed, 125 insertions, 47 deletions
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index a6525404..736384d6 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -345,12 +345,11 @@ class BrowsableAPIRenderer(BaseRenderer):
if not self.show_form_for_method(view, method, request, obj):
return
- if method == 'DELETE' or method == 'OPTIONS':
+ if method in ('DELETE', 'OPTIONS'):
return True # Don't actually need to return a form
if not getattr(view, 'get_serializer', None) or not parsers.FormParser in view.parser_classes:
- media_types = [parser.media_type for parser in view.parser_classes]
- return self.get_generic_content_form(media_types)
+ return
serializer = view.get_serializer(instance=obj)
fields = self.serializer_to_form_fields(serializer)
@@ -422,14 +421,17 @@ class BrowsableAPIRenderer(BaseRenderer):
view = renderer_context['view']
request = renderer_context['request']
response = renderer_context['response']
+ media_types = [parser.media_type for parser in view.parser_classes]
renderer = self.get_default_renderer(view)
content = self.get_content(renderer, data, accepted_media_type, renderer_context)
put_form = self.get_form(view, 'PUT', request)
post_form = self.get_form(view, 'POST', request)
+ patch_form = self.get_form(view, 'PATCH', request)
delete_form = self.get_form(view, 'DELETE', request)
options_form = self.get_form(view, 'OPTIONS', request)
+ generic_content_form = self.get_generic_content_form(media_types)
name = self.get_name(view)
description = self.get_description(view)
@@ -449,8 +451,10 @@ class BrowsableAPIRenderer(BaseRenderer):
'available_formats': [renderer.format for renderer in view.renderer_classes],
'put_form': put_form,
'post_form': post_form,
+ 'patch_form': patch_form,
'delete_form': delete_form,
'options_form': options_form,
+ 'generic_content_form': generic_content_form,
'api_settings': api_settings
})
diff --git a/rest_framework/static/rest_framework/css/default.css b/rest_framework/static/rest_framework/css/default.css
index b2e41b99..73107527 100644
--- a/rest_framework/static/rest_framework/css/default.css
+++ b/rest_framework/static/rest_framework/css/default.css
@@ -150,6 +150,17 @@ html, body {
margin: 0 auto -60px;
}
+.form-switcher {
+ margin-bottom: 0;
+}
+
+.tab-content {
+ padding-top: 25px;
+ background: #fff;
+ border: 1px solid #ddd;
+ border-top: none;
+ border-radius: 0 0 4px 4px;
+}
#footer, #push {
height: 60px; /* .push must be the same height as .footer */
diff --git a/rest_framework/static/rest_framework/js/default.js b/rest_framework/static/rest_framework/js/default.js
index ecaccc0f..484a3bdf 100644
--- a/rest_framework/static/rest_framework/js/default.js
+++ b/rest_framework/static/rest_framework/js/default.js
@@ -3,3 +3,5 @@ prettyPrint();
$('.js-tooltip').tooltip({
delay: 1000
});
+
+$('.form-switcher a:first').tab('show'); \ No newline at end of file
diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html
index 8d807574..2fe7b653 100644
--- a/rest_framework/templates/rest_framework/base.html
+++ b/rest_framework/templates/rest_framework/base.html
@@ -125,54 +125,86 @@
{% if post_form %}
<div class="well">
- <form action="{{ request.get_full_path }}" method="POST" {% if post_form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
- <fieldset>
- {% csrf_token %}
- {{ post_form.non_field_errors }}
- {% for field in post_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" title="Make a POST request on the {{ name }} resource">POST</button>
- </div>
- </fieldset>
- </form>
+ <ul class="nav nav-tabs form-switcher">
+ {% if post_form %}
+ <li><a href="#object-form" data-toggle="tab">HTML</a></li>
+ {% endif %}
+ <li><a href="#generic-content-form" data-toggle="tab">Generic content</a></li>
+ </ul>
+ <div class="tab-content">
+ {% if post_form %}
+ <div class="tab-pane" id="object-form">
+ {% with form=post_form %}
+ <form action="{{ request.get_full_path }}" method="POST" {% if form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
+ <fieldset>
+ {% include "rest_framework/form.html" %}
+ <div class="form-actions">
+ <button class="btn btn-primary" title="Make a POST request on the {{ name }} resource">POST</button>
+ </div>
+ </fieldset>
+ </form>
+ {% endwith %}
+ </div>
+ {% endif %}
+ <div class="tab-pane" id="generic-content-form">
+ {% with form=generic_content_form %}
+ <form action="{{ request.get_full_path }}" method="POST" {% if form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
+ <fieldset>
+ {% include "rest_framework/form.html" %}
+ <div class="form-actions">
+ <button class="btn btn-primary" title="Make a POST request on the {{ name }} resource">POST</button>
+ </div>
+ </fieldset>
+ </form>
+ {% endwith %}
+ </div>
+ </div>
</div>
{% endif %}
- {% if put_form %}
+ {% if 'PUT' in allowed_methods or 'PATCH' in allowed_methods %}
<div class="well">
- <form action="{{ request.get_full_path }}" method="POST" {% if put_form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
- <fieldset>
- <input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PUT" />
- {% csrf_token %}
- {{ 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>
-
- </fieldset>
- </form>
+ <ul class="nav nav-tabs form-switcher">
+ {% if put_form %}
+ <li><a href="#object-form" data-toggle="tab">HTML</a></li>
+ {% endif %}
+ <li><a href="#generic-content-form" data-toggle="tab">Generic content</a></li>
+ </ul>
+ <div class="tab-content">
+ {% if put_form %}
+ <div class="tab-pane" id="object-form">
+ {% with form=put_form %}
+ <form action="{{ request.get_full_path }}" method="POST" {% if form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
+ <fieldset>
+ {% include "rest_framework/form.html" %}
+ <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>
+ </div>
+ </fieldset>
+ </form>
+ {% endwith %}
+ </div>
+ {% endif %}
+ <div class="tab-pane" id="generic-content-form">
+ {% with form=generic_content_form %}
+ <form action="{{ request.get_full_path }}" method="POST" {% if form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
+ <fieldset>
+ {% include "rest_framework/form.html" %}
+ <div class="form-actions">
+ {% if 'PUT' in allowed_methods %}
+ <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>
+ {% endif %}
+ {% if 'PATCH' in allowed_methods %}
+ <button class="btn btn-primary js-tooltip" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PATCH" title="Make a PUT request on the {{ name }} resource">PATCH</button>
+ {% endif %}
+ </div>
+ </fieldset>
+ </form>
+ {% endwith %}
+ </div>
+ </div>
</div>
{% endif %}
-
{% endif %}
</div>
diff --git a/rest_framework/templates/rest_framework/form.html b/rest_framework/templates/rest_framework/form.html
new file mode 100644
index 00000000..dc7acc70
--- /dev/null
+++ b/rest_framework/templates/rest_framework/form.html
@@ -0,0 +1,13 @@
+{% load rest_framework %}
+{% csrf_token %}
+{{ form.non_field_errors }}
+{% for field in 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 %}
diff --git a/rest_framework/tests/renderers.py b/rest_framework/tests/renderers.py
index e3f45ce6..90ef1221 100644
--- a/rest_framework/tests/renderers.py
+++ b/rest_framework/tests/renderers.py
@@ -112,6 +112,9 @@ class POSTDeniedView(APIView):
def put(self, request):
return Response()
+ def patch(self, request):
+ return Response()
+
class DocumentingRendererTests(TestCase):
def test_only_permitted_forms_are_displayed(self):
@@ -120,6 +123,7 @@ class DocumentingRendererTests(TestCase):
response = view(request).render()
self.assertNotContains(response, '>POST<')
self.assertContains(response, '>PUT<')
+ self.assertContains(response, '>PATCH<')
class RendererEndToEndTests(TestCase):
diff --git a/rest_framework/tests/utils.py b/rest_framework/tests/utils.py
index 224c4f9d..8c87917d 100644
--- a/rest_framework/tests/utils.py
+++ b/rest_framework/tests/utils.py
@@ -1,10 +1,10 @@
from __future__ import unicode_literals
-from django.test.client import RequestFactory, FakePayload
+from django.test.client import FakePayload, Client as _Client, RequestFactory as _RequestFactory
from django.test.client import MULTIPART_CONTENT
from rest_framework.compat import urlparse
-class RequestFactory(RequestFactory):
+class RequestFactory(_RequestFactory):
def __init__(self, **defaults):
super(RequestFactory, self).__init__(**defaults)
@@ -26,3 +26,15 @@ class RequestFactory(RequestFactory):
}
r.update(extra)
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