mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-01-23 15:54:16 +03:00
Merge branch 'browsable_api_patch' of https://github.com/wronglink/django-rest-framework into generic-form-input
This commit is contained in:
commit
03afaee423
|
@ -345,12 +345,11 @@ class BrowsableAPIRenderer(BaseRenderer):
|
||||||
if not self.show_form_for_method(view, method, request, obj):
|
if not self.show_form_for_method(view, method, request, obj):
|
||||||
return
|
return
|
||||||
|
|
||||||
if method == 'DELETE' or method == 'OPTIONS':
|
if method in ('DELETE', 'OPTIONS'):
|
||||||
return True # Don't actually need to return a form
|
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:
|
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
|
||||||
return self.get_generic_content_form(media_types)
|
|
||||||
|
|
||||||
serializer = view.get_serializer(instance=obj)
|
serializer = view.get_serializer(instance=obj)
|
||||||
fields = self.serializer_to_form_fields(serializer)
|
fields = self.serializer_to_form_fields(serializer)
|
||||||
|
@ -422,14 +421,17 @@ class BrowsableAPIRenderer(BaseRenderer):
|
||||||
view = renderer_context['view']
|
view = renderer_context['view']
|
||||||
request = renderer_context['request']
|
request = renderer_context['request']
|
||||||
response = renderer_context['response']
|
response = renderer_context['response']
|
||||||
|
media_types = [parser.media_type for parser in view.parser_classes]
|
||||||
|
|
||||||
renderer = self.get_default_renderer(view)
|
renderer = self.get_default_renderer(view)
|
||||||
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)
|
||||||
post_form = self.get_form(view, 'POST', 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)
|
delete_form = self.get_form(view, 'DELETE', request)
|
||||||
options_form = self.get_form(view, 'OPTIONS', request)
|
options_form = self.get_form(view, 'OPTIONS', request)
|
||||||
|
generic_content_form = self.get_generic_content_form(media_types)
|
||||||
|
|
||||||
name = self.get_name(view)
|
name = self.get_name(view)
|
||||||
description = self.get_description(view)
|
description = self.get_description(view)
|
||||||
|
@ -449,8 +451,10 @@ class BrowsableAPIRenderer(BaseRenderer):
|
||||||
'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,
|
||||||
'post_form': post_form,
|
'post_form': post_form,
|
||||||
|
'patch_form': patch_form,
|
||||||
'delete_form': delete_form,
|
'delete_form': delete_form,
|
||||||
'options_form': options_form,
|
'options_form': options_form,
|
||||||
|
'generic_content_form': generic_content_form,
|
||||||
'api_settings': api_settings
|
'api_settings': api_settings
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,17 @@ html, body {
|
||||||
margin: 0 auto -60px;
|
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 {
|
#footer, #push {
|
||||||
height: 60px; /* .push must be the same height as .footer */
|
height: 60px; /* .push must be the same height as .footer */
|
||||||
|
|
|
@ -3,3 +3,5 @@ prettyPrint();
|
||||||
$('.js-tooltip').tooltip({
|
$('.js-tooltip').tooltip({
|
||||||
delay: 1000
|
delay: 1000
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.form-switcher a:first').tab('show');
|
|
@ -125,54 +125,86 @@
|
||||||
|
|
||||||
{% if post_form %}
|
{% if post_form %}
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<form action="{{ request.get_full_path }}" method="POST" {% if post_form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
|
<ul class="nav nav-tabs form-switcher">
|
||||||
<fieldset>
|
{% if post_form %}
|
||||||
{% csrf_token %}
|
<li><a href="#object-form" data-toggle="tab">HTML</a></li>
|
||||||
{{ post_form.non_field_errors }}
|
{% endif %}
|
||||||
{% for field in post_form %}
|
<li><a href="#generic-content-form" data-toggle="tab">Generic content</a></li>
|
||||||
<div class="control-group"> <!--{% if field.errors %}error{% endif %}-->
|
</ul>
|
||||||
{{ field.label_tag|add_class:"control-label" }}
|
<div class="tab-content">
|
||||||
<div class="controls">
|
{% if post_form %}
|
||||||
{{ field }}
|
<div class="tab-pane" id="object-form">
|
||||||
<span class="help-inline">{{ field.help_text }}</span>
|
{% with form=post_form %}
|
||||||
<!--{{ field.errors|add_class:"help-block" }}-->
|
<form action="{{ request.get_full_path }}" method="POST" {% if form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
|
||||||
</div>
|
<fieldset>
|
||||||
</div>
|
{% include "rest_framework/form.html" %}
|
||||||
{% endfor %}
|
<div class="form-actions">
|
||||||
<div class="form-actions">
|
<button class="btn btn-primary" title="Make a POST request on the {{ name }} resource">POST</button>
|
||||||
<button class="btn btn-primary" title="Make a POST request on the {{ name }} resource">POST</button>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
</fieldset>
|
</form>
|
||||||
</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>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if put_form %}
|
{% if 'PUT' in allowed_methods or 'PATCH' in allowed_methods %}
|
||||||
<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-tabs form-switcher">
|
||||||
<fieldset>
|
{% if put_form %}
|
||||||
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PUT" />
|
<li><a href="#object-form" data-toggle="tab">HTML</a></li>
|
||||||
{% csrf_token %}
|
{% endif %}
|
||||||
{{ put_form.non_field_errors }}
|
<li><a href="#generic-content-form" data-toggle="tab">Generic content</a></li>
|
||||||
{% for field in put_form %}
|
</ul>
|
||||||
<div class="control-group"> <!--{% if field.errors %}error{% endif %}-->
|
<div class="tab-content">
|
||||||
{{ field.label_tag|add_class:"control-label" }}
|
{% if put_form %}
|
||||||
<div class="controls">
|
<div class="tab-pane" id="object-form">
|
||||||
{{ field }}
|
{% with form=put_form %}
|
||||||
<span class='help-inline'>{{ field.help_text }}</span>
|
<form action="{{ request.get_full_path }}" method="POST" {% if form.is_multipart %}enctype="multipart/form-data"{% endif %} class="form-horizontal">
|
||||||
<!--{{ field.errors|add_class:"help-block" }}-->
|
<fieldset>
|
||||||
</div>
|
{% include "rest_framework/form.html" %}
|
||||||
</div>
|
<div class="form-actions">
|
||||||
{% endfor %}
|
<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 class="form-actions">
|
</div>
|
||||||
<button class="btn btn-primary js-tooltip" title="Make a PUT request on the {{ name }} resource">PUT</button>
|
</fieldset>
|
||||||
</div>
|
</form>
|
||||||
|
{% endwith %}
|
||||||
</fieldset>
|
</div>
|
||||||
</form>
|
{% 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>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
13
rest_framework/templates/rest_framework/form.html
Normal file
13
rest_framework/templates/rest_framework/form.html
Normal file
|
@ -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 %}
|
|
@ -112,6 +112,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):
|
||||||
|
@ -120,6 +123,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,10 +1,10 @@
|
||||||
from __future__ import unicode_literals
|
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 django.test.client import MULTIPART_CONTENT
|
||||||
from rest_framework.compat import urlparse
|
from rest_framework.compat 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)
|
||||||
|
@ -26,3 +26,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