From 41182c6f060d1ea6a111f61cab713103a84eb38b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 6 Oct 2015 10:58:20 +0100 Subject: [PATCH 1/2] Formns API --- docs/api-guide/renderers.md | 14 ++++++++--- rest_framework/renderers.py | 25 +++++-------------- .../templates/rest_framework/api_form.html | 8 ------ .../templates/rest_framework/base.html | 1 + .../rest_framework/horizontal/form.html | 20 ++++----------- .../templates/rest_framework/inline/form.html | 17 ++++--------- .../rest_framework/vertical/form.html | 16 ++++-------- rest_framework/templatetags/rest_framework.py | 10 ++++++-- 8 files changed, 41 insertions(+), 70 deletions(-) delete mode 100644 rest_framework/templates/rest_framework/api_form.html diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 614259458..89e956d26 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -197,9 +197,17 @@ Note that views that have nested or list serializers for their input won't work ## HTMLFormRenderer -Renders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing `
` tags or an submit actions, as you'll probably need those to include the desired method and URL. Also note that the `HTMLFormRenderer` does not yet support including field error messages. +Renders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing `` tags, a hidden CSRF input or any submit buttons. -**Note**: The `HTMLFormRenderer` class is intended for internal use with the browsable API and admin interface. It should not be considered a fully documented or stable API. The template used by the `HTMLFormRenderer` class, and the context submitted to it **may be subject to change**. If you need to use this renderer class it is advised that you either make a local copy of the class and templates, or follow the release note on REST framework upgrades closely. +This renderer is not intended to be used directly, but can instead be used in templates by passing a serializer instance to the `render_form` template tag. + + {% load rest_framework %} + + + {% csrf_token %} + {% render_form serializer %} + +
**.media_type**: `text/html` @@ -207,7 +215,7 @@ Renders data returned by a serializer into an HTML form. The output of this ren **.charset**: `utf-8` -**.template**: `'rest_framework/form.html'` +**.template**: `'rest_framework/horizontal/form.html'` ## MultiPartRenderer diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index c5b0aeb3c..2c44bd957 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -341,26 +341,16 @@ class HTMLFormRenderer(BaseRenderer): Render serializer data and return an HTML form, as a string. """ form = data.serializer - meta = getattr(form, 'Meta', None) - style = getattr(meta, 'style', {}) + + style = renderer_context.get('style', {}) if 'template_pack' not in style: style['template_pack'] = self.template_pack - if 'base_template' not in style: - style['base_template'] = self.base_template style['renderer'] = self - # This API needs to be finessed and finalized for 3.1 - if 'template' in renderer_context: - template_name = renderer_context['template'] - elif 'template' in style: - template_name = style['template'] - else: - template_name = style['template_pack'].strip('/') + '/' + style['base_template'] - - renderer_context = renderer_context or {} - request = renderer_context['request'] + template_pack = style['template_pack'].strip('/') + template_name = template_pack + '/' + self.base_template template = loader.get_template(template_name) - context = RequestContext(request, { + context = Context({ 'form': form, 'style': style }) @@ -505,10 +495,7 @@ class BrowsableAPIRenderer(BaseRenderer): return form_renderer.render( serializer.data, self.accepted_media_type, - dict( - list(self.renderer_context.items()) + - [('template', 'rest_framework/api_form.html')] - ) + self.renderer_context ) def get_raw_data_form(self, data, view, method, request): diff --git a/rest_framework/templates/rest_framework/api_form.html b/rest_framework/templates/rest_framework/api_form.html deleted file mode 100644 index 166988324..000000000 --- a/rest_framework/templates/rest_framework/api_form.html +++ /dev/null @@ -1,8 +0,0 @@ -{% load rest_framework %} -{% csrf_token %} -{% for field in form %} - {% if not field.read_only %} - {% render_field field style=style %} - {% endif %} -{% endfor %} - diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index 8d21487ee..220bb1d5a 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -154,6 +154,7 @@ {% with form=post_form %}
+ {% csrf_token %} {{ post_form }}
diff --git a/rest_framework/templates/rest_framework/horizontal/form.html b/rest_framework/templates/rest_framework/horizontal/form.html index af16a0eb4..13fc807eb 100644 --- a/rest_framework/templates/rest_framework/horizontal/form.html +++ b/rest_framework/templates/rest_framework/horizontal/form.html @@ -1,16 +1,6 @@ {% load rest_framework %} - - - {% csrf_token %} - {% for field in form %} - {% if not field.read_only %} - {% render_field field style=style %} - {% endif %} - {% endfor %} - -
-
- -
-
- +{% for field in form %} + {% if not field.read_only %} + {% render_field field style=style %} + {% endif %} +{% endfor %} diff --git a/rest_framework/templates/rest_framework/inline/form.html b/rest_framework/templates/rest_framework/inline/form.html index e56e4312b..13fc807eb 100644 --- a/rest_framework/templates/rest_framework/inline/form.html +++ b/rest_framework/templates/rest_framework/inline/form.html @@ -1,13 +1,6 @@ {% load rest_framework %} - -
- {% csrf_token %} - - {% for field in form %} - {% if not field.read_only %} - {% render_field field style=style %} - {% endif %} - {% endfor %} - - -
+{% for field in form %} + {% if not field.read_only %} + {% render_field field style=style %} + {% endif %} +{% endfor %} diff --git a/rest_framework/templates/rest_framework/vertical/form.html b/rest_framework/templates/rest_framework/vertical/form.html index bd57ca19a..13fc807eb 100644 --- a/rest_framework/templates/rest_framework/vertical/form.html +++ b/rest_framework/templates/rest_framework/vertical/form.html @@ -1,12 +1,6 @@ {% load rest_framework %} - -
- {% csrf_token %} - {% for field in form %} - {% if not field.read_only %} - {% render_field field style=style %} - {% endif %} - {% endfor %} - - -
+{% for field in form %} + {% if not field.read_only %} + {% render_field field style=style %} + {% endif %} +{% endfor %} diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index 08acecef7..b584fdbaf 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -25,8 +25,14 @@ def get_pagination_html(pager): @register.simple_tag -def render_field(field, style=None): - style = style or {} +def render_form(serializer, template_pack=None): + style = {'template_pack': template_pack} if template_pack else {} + renderer = HTMLFormRenderer() + return renderer.render(serializer.data, None, {'style': style}) + + +@register.simple_tag +def render_field(field, style): renderer = style.get('renderer', HTMLFormRenderer()) return renderer.render_field(field, style) From 90247afe297db263f849a359a48af74b5a04a665 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 12 Oct 2015 21:14:58 +0100 Subject: [PATCH 2/2] Docs on Forms API --- docs/api-guide/fields.md | 7 +- docs/api-guide/renderers.md | 3 + docs/img/horizontal.png | Bin 0 -> 11628 bytes docs/img/inline.png | Bin 0 -> 8295 bytes docs/img/vertical.png | Bin 0 -> 11946 bytes docs/index.md | 5 +- docs/topics/html-and-forms.md | 214 ++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + rest_framework/renderers.py | 4 +- 9 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 docs/img/horizontal.png create mode 100644 docs/img/inline.png create mode 100644 docs/img/vertical.png create mode 100644 docs/topics/html-and-forms.md diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index f8be0c1b9..c24f20361 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -85,9 +85,9 @@ A value that should be used for pre-populating the value of HTML form fields. ### `style` -A dictionary of key-value pairs that can be used to control how renderers should render the field. The API for this should still be considered experimental, and will be formalized with the 3.1 release. +A dictionary of key-value pairs that can be used to control how renderers should render the field. -Two options are currently used in HTML form generation, `'input_type'` and `'base_template'`. +Two examples here are `'input_type'` and `'base_template'`: # Use for the input. password = serializers.CharField( @@ -100,7 +100,7 @@ Two options are currently used in HTML form generation, `'input_type'` and `'bas style = {'base_template': 'radio.html'} } -**Note**: The `style` argument replaces the old-style version 2.x `widget` keyword argument. Because REST framework 3 now uses templated HTML form generation, the `widget` option that was used to support Django built-in widgets can no longer be supported. Version 3.3 is planned to include public API support for customizing HTML form generation. +For more details see the [HTML & Forms][html-and-forms] documentation. --- @@ -658,6 +658,7 @@ The [django-rest-framework-gis][django-rest-framework-gis] package provides geog The [django-rest-framework-hstore][django-rest-framework-hstore] package provides an `HStoreField` to support [django-hstore][django-hstore] `DictionaryField` model field. [cite]: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.cleaned_data +[html-and-forms]: ../topics/html-and-forms.md [FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS [ecma262]: http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 [strftime]: http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 89e956d26..fecdee8a4 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -209,6 +209,8 @@ This renderer is not intended to be used directly, but can instead be used in te +For more information see the [HTML & Forms][html-and-forms] documentation. + **.media_type**: `text/html` **.format**: `'.form'` @@ -463,6 +465,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily [cite]: https://docs.djangoproject.com/en/dev/ref/template-response/#the-rendering-process [conneg]: content-negotiation.md +[html-and-forms]: ../topics/html-and-forms.md [browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers [testing]: testing.md [HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas diff --git a/docs/img/horizontal.png b/docs/img/horizontal.png new file mode 100644 index 0000000000000000000000000000000000000000..3852070aeb4f168b9ab2e9561d4c4e9f6bc3db09 GIT binary patch literal 11628 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 b>;>u+Z!iCigop9#Kbpu$D2P{z8V3Fkpl~2C literal 0 HcmV?d00001 diff --git a/docs/img/inline.png b/docs/img/inline.png new file mode 100644 index 0000000000000000000000000000000000000000..a910de19efd4670f61a5f445ff658ff632f27e55 GIT binary patch literal 8295 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 G@P7aa-F9LC literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md index 4617d0bcc..bf849bd7b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -191,7 +191,9 @@ The API guide is your complete reference manual to all the functionality provide General guides to using REST framework. * [Documenting your API][documenting-your-api] +* [Internationalization][internationalization] * [AJAX, CSRF & CORS][ajax-csrf-cors] +* [HTML & Forms][html-and-forms] * [Browser enhancements][browser-enhancements] * [The Browsable API][browsableapi] * [REST, Hypermedia & HATEOAS][rest-hypermedia-hateoas] @@ -303,8 +305,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [settings]: api-guide/settings.md [documenting-your-api]: topics/documenting-your-api.md -[internationalization]: topics/documenting-your-api.md +[internationalization]: topics/internationalization.md [ajax-csrf-cors]: topics/ajax-csrf-cors.md +[html-and-forms]: topics/html-and-forms.md [browser-enhancements]: topics/browser-enhancements.md [browsableapi]: topics/browsable-api.md [rest-hypermedia-hateoas]: topics/rest-hypermedia-hateoas.md diff --git a/docs/topics/html-and-forms.md b/docs/topics/html-and-forms.md new file mode 100644 index 000000000..b9ad0c651 --- /dev/null +++ b/docs/topics/html-and-forms.md @@ -0,0 +1,214 @@ +# HTML & Forms + +REST framework is suitable for returning both API style responses, and regular HTML pages. Additionally, serializers can used as HTML forms and rendered in templates. + +## Rendering HTML + +In order to return HTML responses you'll need to either `TemplateHTMLRenderer`, or `StaticHTMLRenderer`. + +The `TemplateHTMLRenderer` class expects the response to contain a dictionary of context data, and renders an HTML page based on a template that must be specified either in the view or on the response. + +The `StaticHTMLRender` class expects the response to contain a string of the pre-rendered HTML content. + +Because static HTML pages typically have different behavior from API responses you'll probably need to write any HTML views explicitly, rather than relying on the built-in generic views. + +Here's an example of a view that returns a list of "Profile" instances, rendered in an HTML template: + +**views.py**: + + from my_project.example.models import Profile + from rest_framework.renderers import TemplateHTMLRenderer + from rest_framework.views import APIView + + + class ProfileList(APIView): + renderer_classes = [TemplateHTMLRenderer] + template_name = 'profile_list.html' + + def get(self, request): + queryset = Profile.objects.all() + return Response({'profiles': queryset}) + +**profile_list.html**: + + +

Profiles

+
    + {% for profile in profiles %} +
  • {{ profile.name }}
  • + {% endfor %} +
+ + +## Rendering Forms + +Serializers may be rendered as forms by using the `render_form` template tag, and including the serializer instance as context to the template. + +The following view demonstrates an example of using a serializer in a template for viewing and updating a model instance: + +**views.py**: + + from django.shortcuts import get_object_or_404 + from my_project.example.models import Profile + from rest_framework.renderers import TemplateHTMLRenderer + from rest_framework.views import APIView + + + class ProfileDetail(APIView): + renderer_classes = [TemplateHTMLRenderer] + template_name = 'profile_detail.html' + + def get(self, request, pk): + profile = get_object_or_404(Profile, pk=pk) + serializer = ProfileSerializer(profile) + return Response({'serializer': serializer, 'profile': profile}) + + def post(self, request, pk): + profile = get_object_or_404(Profile, pk=pk) + serializer = ProfileSerializer(profile) + if not serializer.is_valid(): + return Response({'serializer': serializer, 'profile': profile}) return redirect('profile-list') + +**profile_detail.html**: + + {% load rest_framework %} + + + +

Profile - {{ profile.name }}

+ +
+ {% csrf_token %} + {% render_form serializer %} + +
+ + + +### Using template packs + +The `render_form` tag takes an optional `template_pack` argument, that specifies which template directory should be used for rendering the form and form fields. + +REST framework includes three built-in template packs, all based on Bootstrap 3. The built-in styles are `horizontal`, `vertical`, and `inline`. The default style is `horizontal`. To use any of these template packs you'll want to also include the Bootstrap 3 CSS. + +The following HTML will link to a CDN hosted version of the Bootstrap 3 CSS: + + + … + + + +Third party packages may include alternate template packs, by bundling a template directory containing the necessary form and field templates. + +Let's take a look at how to render each of the three available template packs. For these examples we'll use a single serializer class to present a "Login" form. + + class LoginSerializer(serializers.Serializer): + email = serializers.EmailField( + max_length=100, + style={'placeholder': 'Email'} + ) + password = serializers.CharField( + max_length=100, + style={'input_type': 'password', 'placeholder': 'Password'} + ) + remember_me = serializers.BooleanField() --- + +#### `rest_framework/vertical` + +Presents form labels above their corresponding control inputs, using the standard Bootstrap layout. + +*This is the default template pack.* + + {% load rest_framework %} + + ... + +
+ {% csrf_token %} + {% render_form serializer template_pack='rest_framework/vertical' %} + +
+ +![Vertical form example](../img/vertical.png) + +--- + #### `rest_framework/horizontal` + +Presents labels and controls alongside each other, using a 2/10 column split. + +*This is the form style used in the browsable API and admin renderers.* + + {% load rest_framework %} + + ... + +
+ {% csrf_token %} + {% render_form serializer %} +
+
+ +
+
+
+ +![Horizontal form example](../img/horizontal.png) + +--- + +#### `rest_framework/inline` + +A compact form style that presents all the controls inline. + + {% load rest_framework %} + + ... + +
+ {% csrf_token %} + {% render_form serializer template_pack='rest_framework/inline' %} + +
+ +![Inline form example](../img/inline.png) + +## Field styles + +Serializer fields can have their rendering style customized by using the `style` keyword argument. This argument is a dictionary of options that control the template and layout used. + +The most common way to customize the field style is to use the `base_template` style keyword argument to select which template in the template pack should be use. + +For example, to render a `CharField` as an HTML textarea rather than the default HTML input, you would use something like this: + + details = serializers.CharField( + max_length=1000, + style={'base_template': 'textarea.html'} + ) + +If you instead want a field to be rendered using a custom template that is *not part of an included template pack*, you can instead use the `template` style option, to fully specify a template name: + + details = serializers.CharField( + max_length=1000, + style={'template': 'my-field-templates/custom-input.html'} + ) + +Field templates can also use additional style properties, depending on their type. For example, the `textarea.html` template also accepts a `rows` property that can be used to affect the sizing of the control. + + details = serializers.CharField( + max_length=1000, + style={'base_template': 'textarea.html', 'rows': 10} + ) + +The complete list of `base_template` options and their associated style options is listed below. + +base_template | Valid field types | Additional style options +----|----|---- +input.html | Any string, numeric or date/time field | input_type, placeholder, hide_label +textarea.html | `CharField` | rows, placeholder, hide_label +select.html | `ChoiceField` or relational field types | hide_label +radio.html | `ChoiceField` or relational field types | inline, hide_label +select_multiple.html | `MultipleChoiceField` or relational fields with `many=True` | hide_label +checkbox_multiple.html | `MultipleChoiceField` or relational fields with `many=True` | inline, hide_label +checkbox.html | `BooleanField` | hide_label +fieldset.html | Nested serializer | hide_label +list_fieldset.html | `ListField` or nested serializer with `many=True` | hide_label diff --git a/mkdocs.yml b/mkdocs.yml index f2673ee03..082bd4b36 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -47,6 +47,7 @@ pages: - 'Documenting your API': 'topics/documenting-your-api.md' - 'Internationalization': 'topics/internationalization.md' - 'AJAX, CSRF & CORS': 'topics/ajax-csrf-cors.md' + - 'HTML & Forms': 'topics/html-and-forms.md' - 'Browser Enhancements': 'topics/browser-enhancements.md' - 'The Browsable API': 'topics/browsable-api.md' - 'REST, Hypermedia & HATEOAS': 'topics/rest-hypermedia-hateoas.md' diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 2c44bd957..ade749e3f 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -249,7 +249,7 @@ class HTMLFormRenderer(BaseRenderer): media_type = 'text/html' format = 'form' charset = 'utf-8' - template_pack = 'rest_framework/horizontal/' + template_pack = 'rest_framework/vertical/' base_template = 'form.html' default_style = ClassLookupDict({ @@ -495,7 +495,7 @@ class BrowsableAPIRenderer(BaseRenderer): return form_renderer.render( serializer.data, self.accepted_media_type, - self.renderer_context + {'style': {'template_pack': 'rest_framework/horizontal'}} ) def get_raw_data_form(self, data, view, method, request):