aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2014-10-15 15:13:28 +0100
committerTom Christie2014-10-15 15:13:28 +0100
commitb4f3379c7002f0c80a26605fdd9c69d7cef2f16f (patch)
tree2a4939cc2c46c9fdfab4284ec29aa9be8925d710
parente8ea365c15b13185efc2ba03e57a3302f783d538 (diff)
downloaddjango-rest-framework-b4f3379c7002f0c80a26605fdd9c69d7cef2f16f.tar.bz2
Support fields that reference a simple callable
-rw-r--r--rest_framework/fields.py4
-rw-r--r--rest_framework/renderers.py105
-rw-r--r--rest_framework/serializers.py4
-rw-r--r--rest_framework/static/rest_framework/css/bootstrap-tweaks.css4
-rw-r--r--rest_framework/static/rest_framework/css/default.css4
-rw-r--r--rest_framework/templatetags/rest_framework.py7
6 files changed, 78 insertions, 50 deletions
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index b881ad13..24dfaaf5 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -66,6 +66,8 @@ def get_attribute(instance, attrs):
return instance[attr]
except (KeyError, TypeError, AttributeError):
raise exc
+ if is_simple_callable(instance):
+ return instance()
return instance
@@ -1025,8 +1027,6 @@ class ReadOnlyField(Field):
super(ReadOnlyField, self).__init__(**kwargs)
def to_representation(self, value):
- if is_simple_callable(value):
- return value()
return value
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index 5fae75f2..d5defa31 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -340,76 +340,101 @@ class HTMLFormRenderer(BaseRenderer):
media_type = 'text/html'
format = 'form'
charset = 'utf-8'
+ template_pack = 'rest_framework/horizontal/'
+ base_template = 'form.html'
- field_templates = ClassLookupDict({
+ default_style = ClassLookupDict({
serializers.Field: {
- 'default': 'input.html'
+ 'base_template': 'input.html',
+ 'input_type': 'text'
},
- serializers.BooleanField: {
- 'default': 'checkbox.html'
+ serializers.EmailField: {
+ 'base_template': 'input.html',
+ 'input_type': 'email'
+ },
+ serializers.URLField: {
+ 'base_template': 'input.html',
+ 'input_type': 'url'
+ },
+ serializers.IntegerField: {
+ 'base_template': 'input.html',
+ 'input_type': 'number'
+ },
+ serializers.DateTimeField: {
+ 'base_template': 'input.html',
+ 'input_type': 'datetime-local'
},
- serializers.CharField: {
- 'default': 'input.html',
- 'textarea': 'textarea.html'
+ serializers.DateField: {
+ 'base_template': 'input.html',
+ 'input_type': 'date'
+ },
+ serializers.TimeField: {
+ 'base_template': 'input.html',
+ 'input_type': 'time'
+ },
+ serializers.BooleanField: {
+ 'base_template': 'checkbox.html'
},
serializers.ChoiceField: {
- 'default': 'select.html',
- 'radio': 'select_radio.html'
+ 'base_template': 'select.html', # Also valid: 'radio.html'
},
serializers.MultipleChoiceField: {
- 'default': 'select_multiple.html',
- 'checkbox': 'select_checkbox.html'
+ 'base_template': 'select_multiple.html', # Also valid: 'checkbox_multiple.html'
},
serializers.ManyRelation: {
- 'default': 'select_multiple.html',
- 'checkbox': 'select_checkbox.html'
+ 'base_template': 'select_multiple.html', # Also valid: 'checkbox_multiple.html'
},
serializers.Serializer: {
- 'default': 'fieldset.html'
+ 'base_template': 'fieldset.html'
},
serializers.ListSerializer: {
- 'default': 'list_fieldset.html'
+ 'base_template': 'list_fieldset.html'
}
})
- input_type = ClassLookupDict({
- serializers.Field: 'text',
- serializers.EmailField: 'email',
- serializers.URLField: 'url',
- serializers.IntegerField: 'number',
- serializers.DateTimeField: 'datetime-local',
- serializers.DateField: 'date',
- serializers.TimeField: 'time',
- })
+ def render_field(self, field, parent_style):
+ style = dict(self.default_style[field])
+ style.update(field.style)
+ if 'template_pack' not in style:
+ style['template_pack'] = parent_style['template_pack']
+ style['renderer'] = self
- def render_field(self, field, template_pack=None):
- style_type = field.style.get('type', 'default')
-
- input_type = self.input_type[field]
- if input_type == 'datetime-local' and isinstance(field.value, six.text_type):
+ if style.get('input_type') == 'datetime-local' and isinstance(field.value, six.text_type):
field.value = field.value.rstrip('Z')
- base = self.field_templates[field][style_type]
- template_name = template_pack + '/fields/' + base
+ if 'template' in style:
+ template_name = style['template']
+ else:
+ template_name = style['template_pack'].strip('/') + '/' + style['base_template']
+
template = loader.get_template(template_name)
- context = Context({
- 'field': field,
- 'input_type': input_type,
- 'renderer': self,
- })
+ context = Context({'field': field, 'style': style})
return template.render(context)
def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Render serializer data and return an HTML form, as a string.
"""
+ form = data.serializer
+ meta = getattr(form, 'Meta', None)
+ style = getattr(meta, '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
+
+ if '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 = loader.get_template('rest_framework/horizontal/form.html')
+ template = loader.get_template(template_name)
context = RequestContext(request, {
- 'form': data.serializer,
- 'template_pack': 'rest_framework/horizontal',
- 'renderer': self
+ 'form': form,
+ 'style': style
})
return template.render(context)
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index c844605f..534be6f9 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -115,7 +115,7 @@ class BaseSerializer(Field):
@property
def data(self):
if not hasattr(self, '_data'):
- if self.instance is not None:
+ if self.instance is not None and not getattr(self, '_errors', None):
self._data = self.to_representation(self.instance)
else:
self._data = self.get_initial()
@@ -339,7 +339,7 @@ class Serializer(BaseSerializer):
Dict of native values <- Dict of primitive datatypes.
"""
ret = {}
- errors = {}
+ errors = ReturnDict(serializer=self)
fields = [field for field in self.fields.values() if not field.read_only]
for field in fields:
diff --git a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css
index 6a37cae2..36c7be48 100644
--- a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css
+++ b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css
@@ -115,10 +115,6 @@ html, body {
margin-bottom: 0;
}
-.well form .help-block {
- color: #999999;
-}
-
.nav-tabs {
border: 0;
}
diff --git a/rest_framework/static/rest_framework/css/default.css b/rest_framework/static/rest_framework/css/default.css
index 82c6033b..4f52cc56 100644
--- a/rest_framework/static/rest_framework/css/default.css
+++ b/rest_framework/static/rest_framework/css/default.css
@@ -36,6 +36,10 @@ ul.breadcrumb {
margin: 70px 0 0 0;
}
+.breadcrumb li.active a {
+ color: #777;
+}
+
form select, form input, form textarea {
width: 90%;
}
diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py
index c092d39f..d9424f02 100644
--- a/rest_framework/templatetags/rest_framework.py
+++ b/rest_framework/templatetags/rest_framework.py
@@ -8,6 +8,7 @@ from django.utils.html import escape
from django.utils.safestring import SafeData, mark_safe
from django.utils.html import smart_urlquote
from rest_framework.compat import urlparse, force_text
+from rest_framework.renderers import HTMLFormRenderer
import re
register = template.Library()
@@ -32,8 +33,10 @@ class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])')
# And the template tags themselves...
@register.simple_tag
-def render_field(field, template_pack=None, renderer=None):
- return renderer.render_field(field, template_pack)
+def render_field(field, style=None):
+ style = style or {}
+ renderer = style.get('renderer', HTMLFormRenderer())
+ return renderer.render_field(field, style)
@register.simple_tag