mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 17:47:04 +03:00
Serializers can now be rendered directly to HTML
This commit is contained in:
parent
1fd83adb9c
commit
a14f1e8864
|
@ -123,6 +123,7 @@ class Field(object):
|
||||||
use_files = False
|
use_files = False
|
||||||
form_field_class = forms.CharField
|
form_field_class = forms.CharField
|
||||||
type_label = 'field'
|
type_label = 'field'
|
||||||
|
widget = None
|
||||||
|
|
||||||
def __init__(self, source=None, label=None, help_text=None):
|
def __init__(self, source=None, label=None, help_text=None):
|
||||||
self.parent = None
|
self.parent = None
|
||||||
|
@ -134,9 +135,29 @@ class Field(object):
|
||||||
|
|
||||||
if label is not None:
|
if label is not None:
|
||||||
self.label = smart_text(label)
|
self.label = smart_text(label)
|
||||||
|
else:
|
||||||
|
self.label = None
|
||||||
|
|
||||||
if help_text is not None:
|
if help_text is not None:
|
||||||
self.help_text = strip_multiple_choice_msg(smart_text(help_text))
|
self.help_text = strip_multiple_choice_msg(smart_text(help_text))
|
||||||
|
else:
|
||||||
|
self.help_text = None
|
||||||
|
|
||||||
|
self._errors = []
|
||||||
|
self._value = None
|
||||||
|
self._name = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def errors(self):
|
||||||
|
return self._errors
|
||||||
|
|
||||||
|
def widget_html(self):
|
||||||
|
if not self.widget:
|
||||||
|
return ''
|
||||||
|
return self.widget.render(self._name, self._value)
|
||||||
|
|
||||||
|
def label_tag(self):
|
||||||
|
return '<label for="%s">%s:</label>' % (self._name, self.label)
|
||||||
|
|
||||||
def initialize(self, parent, field_name):
|
def initialize(self, parent, field_name):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -336,71 +336,15 @@ class HTMLFormRenderer(BaseRenderer):
|
||||||
template = 'rest_framework/form.html'
|
template = 'rest_framework/form.html'
|
||||||
charset = 'utf-8'
|
charset = 'utf-8'
|
||||||
|
|
||||||
def data_to_form_fields(self, data):
|
|
||||||
fields = {}
|
|
||||||
for key, val in data.fields.items():
|
|
||||||
if getattr(val, 'read_only', True):
|
|
||||||
# Don't include read-only fields.
|
|
||||||
continue
|
|
||||||
|
|
||||||
if getattr(val, 'fields', None):
|
|
||||||
# Nested data not supported by HTML forms.
|
|
||||||
continue
|
|
||||||
|
|
||||||
kwargs = {}
|
|
||||||
kwargs['required'] = val.required
|
|
||||||
|
|
||||||
#if getattr(v, 'queryset', None):
|
|
||||||
# kwargs['queryset'] = v.queryset
|
|
||||||
|
|
||||||
if getattr(val, 'choices', None) is not None:
|
|
||||||
kwargs['choices'] = val.choices
|
|
||||||
|
|
||||||
if getattr(val, 'regex', None) is not None:
|
|
||||||
kwargs['regex'] = val.regex
|
|
||||||
|
|
||||||
if getattr(val, 'widget', None):
|
|
||||||
widget = copy.deepcopy(val.widget)
|
|
||||||
kwargs['widget'] = widget
|
|
||||||
|
|
||||||
if getattr(val, 'default', None) is not None:
|
|
||||||
kwargs['initial'] = val.default
|
|
||||||
|
|
||||||
if getattr(val, 'label', None) is not None:
|
|
||||||
kwargs['label'] = val.label
|
|
||||||
|
|
||||||
if getattr(val, 'help_text', None) is not None:
|
|
||||||
kwargs['help_text'] = val.help_text
|
|
||||||
|
|
||||||
fields[key] = val.form_field_class(**kwargs)
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
def render(self, data, accepted_media_type=None, renderer_context=None):
|
def render(self, data, accepted_media_type=None, renderer_context=None):
|
||||||
"""
|
"""
|
||||||
Render serializer data and return an HTML form, as a string.
|
Render serializer data and return an HTML form, as a string.
|
||||||
"""
|
"""
|
||||||
# The HTMLFormRenderer currently uses something of a hack to render
|
renderer_context = renderer_context or {}
|
||||||
# the content, by translating each of the serializer fields into
|
request = renderer_context['request']
|
||||||
# an html form field, creating a dynamic form using those fields,
|
|
||||||
# and then rendering that form.
|
|
||||||
|
|
||||||
# This isn't strictly neccessary, as we could render the serilizer
|
|
||||||
# fields to HTML directly. The implementation is historical and will
|
|
||||||
# likely change at some point.
|
|
||||||
|
|
||||||
self.renderer_context = renderer_context or {}
|
|
||||||
request = self.renderer_context['request']
|
|
||||||
|
|
||||||
# Creating an on the fly form see:
|
|
||||||
# http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python
|
|
||||||
fields = self.data_to_form_fields(data)
|
|
||||||
DynamicForm = type(str('DynamicForm'), (forms.Form,), fields)
|
|
||||||
data = None if data.empty else data
|
|
||||||
|
|
||||||
template = loader.get_template(self.template)
|
template = loader.get_template(self.template)
|
||||||
context = RequestContext(request, {'form': DynamicForm(data)})
|
context = RequestContext(request, {'form': data})
|
||||||
|
|
||||||
return template.render(context)
|
return template.render(context)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,13 @@ from rest_framework.relations import *
|
||||||
from rest_framework.fields import *
|
from rest_framework.fields import *
|
||||||
|
|
||||||
|
|
||||||
|
def pretty_name(name):
|
||||||
|
"""Converts 'first_name' to 'First name'"""
|
||||||
|
if not name:
|
||||||
|
return ''
|
||||||
|
return name.replace('_', ' ').capitalize()
|
||||||
|
|
||||||
|
|
||||||
class RelationsList(list):
|
class RelationsList(list):
|
||||||
_deleted = []
|
_deleted = []
|
||||||
|
|
||||||
|
@ -306,7 +313,17 @@ class BaseSerializer(WritableField):
|
||||||
for field_name, field in self.fields.items():
|
for field_name, field in self.fields.items():
|
||||||
field.initialize(parent=self, field_name=field_name)
|
field.initialize(parent=self, field_name=field_name)
|
||||||
key = self.get_field_key(field_name)
|
key = self.get_field_key(field_name)
|
||||||
value = field.field_to_native(obj, field_name)
|
if self._errors:
|
||||||
|
value = self.init_data.get(field_name)
|
||||||
|
else:
|
||||||
|
value = field.field_to_native(obj, field_name)
|
||||||
|
|
||||||
|
field._errors = self._errors.get(key) if self._errors else None
|
||||||
|
field._name = field_name
|
||||||
|
field._value = value
|
||||||
|
if not field.label:
|
||||||
|
field.label = pretty_name(key)
|
||||||
|
|
||||||
ret[key] = value
|
ret[key] = value
|
||||||
ret.fields[key] = field
|
ret.fields[key] = field
|
||||||
return ret
|
return ret
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
{% load rest_framework %}
|
{% load rest_framework %}
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.non_field_errors }}
|
{{ form.non_field_errors }}
|
||||||
{% for field in form %}
|
{% for field in form.fields.values %}
|
||||||
<div class="control-group"> <!--{% if field.errors %}error{% endif %}-->
|
{% if not field.read_only %}
|
||||||
|
<div class="control-group {% if field.errors %}error{% endif %}">
|
||||||
{{ field.label_tag|add_class:"control-label" }}
|
{{ field.label_tag|add_class:"control-label" }}
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
{{ field }}
|
{{ field.widget_html }}
|
||||||
<span class="help-block">{{ field.help_text }}</span>
|
{% if field.help_text %}<span class="help-block">{{ field.help_text }}</span>{% endif %}
|
||||||
<!--{{ field.errors|add_class:"help-block" }}-->
|
{% for error in field.errors %}<span class="help-block">{{ error }}</span>{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user