mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-13 05:06:53 +03:00
Support fields that reference a simple callable
This commit is contained in:
parent
e8ea365c15
commit
b4f3379c70
|
@ -66,6 +66,8 @@ def get_attribute(instance, attrs):
|
||||||
return instance[attr]
|
return instance[attr]
|
||||||
except (KeyError, TypeError, AttributeError):
|
except (KeyError, TypeError, AttributeError):
|
||||||
raise exc
|
raise exc
|
||||||
|
if is_simple_callable(instance):
|
||||||
|
return instance()
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
@ -1025,8 +1027,6 @@ class ReadOnlyField(Field):
|
||||||
super(ReadOnlyField, self).__init__(**kwargs)
|
super(ReadOnlyField, self).__init__(**kwargs)
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
if is_simple_callable(value):
|
|
||||||
return value()
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -340,76 +340,101 @@ class HTMLFormRenderer(BaseRenderer):
|
||||||
media_type = 'text/html'
|
media_type = 'text/html'
|
||||||
format = 'form'
|
format = 'form'
|
||||||
charset = 'utf-8'
|
charset = 'utf-8'
|
||||||
|
template_pack = 'rest_framework/horizontal/'
|
||||||
|
base_template = 'form.html'
|
||||||
|
|
||||||
field_templates = ClassLookupDict({
|
default_style = ClassLookupDict({
|
||||||
serializers.Field: {
|
serializers.Field: {
|
||||||
'default': 'input.html'
|
'base_template': 'input.html',
|
||||||
|
'input_type': 'text'
|
||||||
|
},
|
||||||
|
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.DateField: {
|
||||||
|
'base_template': 'input.html',
|
||||||
|
'input_type': 'date'
|
||||||
|
},
|
||||||
|
serializers.TimeField: {
|
||||||
|
'base_template': 'input.html',
|
||||||
|
'input_type': 'time'
|
||||||
},
|
},
|
||||||
serializers.BooleanField: {
|
serializers.BooleanField: {
|
||||||
'default': 'checkbox.html'
|
'base_template': 'checkbox.html'
|
||||||
},
|
|
||||||
serializers.CharField: {
|
|
||||||
'default': 'input.html',
|
|
||||||
'textarea': 'textarea.html'
|
|
||||||
},
|
},
|
||||||
serializers.ChoiceField: {
|
serializers.ChoiceField: {
|
||||||
'default': 'select.html',
|
'base_template': 'select.html', # Also valid: 'radio.html'
|
||||||
'radio': 'select_radio.html'
|
|
||||||
},
|
},
|
||||||
serializers.MultipleChoiceField: {
|
serializers.MultipleChoiceField: {
|
||||||
'default': 'select_multiple.html',
|
'base_template': 'select_multiple.html', # Also valid: 'checkbox_multiple.html'
|
||||||
'checkbox': 'select_checkbox.html'
|
|
||||||
},
|
},
|
||||||
serializers.ManyRelation: {
|
serializers.ManyRelation: {
|
||||||
'default': 'select_multiple.html',
|
'base_template': 'select_multiple.html', # Also valid: 'checkbox_multiple.html'
|
||||||
'checkbox': 'select_checkbox.html'
|
|
||||||
},
|
},
|
||||||
serializers.Serializer: {
|
serializers.Serializer: {
|
||||||
'default': 'fieldset.html'
|
'base_template': 'fieldset.html'
|
||||||
},
|
},
|
||||||
serializers.ListSerializer: {
|
serializers.ListSerializer: {
|
||||||
'default': 'list_fieldset.html'
|
'base_template': 'list_fieldset.html'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
input_type = ClassLookupDict({
|
def render_field(self, field, parent_style):
|
||||||
serializers.Field: 'text',
|
style = dict(self.default_style[field])
|
||||||
serializers.EmailField: 'email',
|
style.update(field.style)
|
||||||
serializers.URLField: 'url',
|
if 'template_pack' not in style:
|
||||||
serializers.IntegerField: 'number',
|
style['template_pack'] = parent_style['template_pack']
|
||||||
serializers.DateTimeField: 'datetime-local',
|
style['renderer'] = self
|
||||||
serializers.DateField: 'date',
|
|
||||||
serializers.TimeField: 'time',
|
|
||||||
})
|
|
||||||
|
|
||||||
def render_field(self, field, template_pack=None):
|
if style.get('input_type') == 'datetime-local' and isinstance(field.value, six.text_type):
|
||||||
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):
|
|
||||||
field.value = field.value.rstrip('Z')
|
field.value = field.value.rstrip('Z')
|
||||||
|
|
||||||
base = self.field_templates[field][style_type]
|
if 'template' in style:
|
||||||
template_name = template_pack + '/fields/' + base
|
template_name = style['template']
|
||||||
|
else:
|
||||||
|
template_name = style['template_pack'].strip('/') + '/' + style['base_template']
|
||||||
|
|
||||||
template = loader.get_template(template_name)
|
template = loader.get_template(template_name)
|
||||||
context = Context({
|
context = Context({'field': field, 'style': style})
|
||||||
'field': field,
|
|
||||||
'input_type': input_type,
|
|
||||||
'renderer': self,
|
|
||||||
})
|
|
||||||
return template.render(context)
|
return template.render(context)
|
||||||
|
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
|
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 {}
|
renderer_context = renderer_context or {}
|
||||||
request = renderer_context['request']
|
request = renderer_context['request']
|
||||||
template = loader.get_template('rest_framework/horizontal/form.html')
|
template = loader.get_template(template_name)
|
||||||
context = RequestContext(request, {
|
context = RequestContext(request, {
|
||||||
'form': data.serializer,
|
'form': form,
|
||||||
'template_pack': 'rest_framework/horizontal',
|
'style': style
|
||||||
'renderer': self
|
|
||||||
})
|
})
|
||||||
return template.render(context)
|
return template.render(context)
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ class BaseSerializer(Field):
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
if not hasattr(self, '_data'):
|
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)
|
self._data = self.to_representation(self.instance)
|
||||||
else:
|
else:
|
||||||
self._data = self.get_initial()
|
self._data = self.get_initial()
|
||||||
|
@ -339,7 +339,7 @@ class Serializer(BaseSerializer):
|
||||||
Dict of native values <- Dict of primitive datatypes.
|
Dict of native values <- Dict of primitive datatypes.
|
||||||
"""
|
"""
|
||||||
ret = {}
|
ret = {}
|
||||||
errors = {}
|
errors = ReturnDict(serializer=self)
|
||||||
fields = [field for field in self.fields.values() if not field.read_only]
|
fields = [field for field in self.fields.values() if not field.read_only]
|
||||||
|
|
||||||
for field in fields:
|
for field in fields:
|
||||||
|
|
|
@ -115,10 +115,6 @@ html, body {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.well form .help-block {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tabs {
|
.nav-tabs {
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,10 @@ ul.breadcrumb {
|
||||||
margin: 70px 0 0 0;
|
margin: 70px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.breadcrumb li.active a {
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
form select, form input, form textarea {
|
form select, form input, form textarea {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ from django.utils.html import escape
|
||||||
from django.utils.safestring import SafeData, mark_safe
|
from django.utils.safestring import SafeData, mark_safe
|
||||||
from django.utils.html import smart_urlquote
|
from django.utils.html import smart_urlquote
|
||||||
from rest_framework.compat import urlparse, force_text
|
from rest_framework.compat import urlparse, force_text
|
||||||
|
from rest_framework.renderers import HTMLFormRenderer
|
||||||
import re
|
import re
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
@ -32,8 +33,10 @@ class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])')
|
||||||
# And the template tags themselves...
|
# And the template tags themselves...
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def render_field(field, template_pack=None, renderer=None):
|
def render_field(field, style=None):
|
||||||
return renderer.render_field(field, template_pack)
|
style = style or {}
|
||||||
|
renderer = style.get('renderer', HTMLFormRenderer())
|
||||||
|
return renderer.render_field(field, style)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
|
|
Loading…
Reference in New Issue
Block a user