First pass on incorperating the form rendering into the browsable API

This commit is contained in:
Tom Christie 2014-10-02 16:24:24 +01:00
parent ffc6aa3abc
commit df7b6fcf58
32 changed files with 225 additions and 152 deletions

View File

@ -689,10 +689,10 @@ class DateTimeField(Field):
return value
if self.format.lower() == ISO_8601:
ret = value.isoformat()
if ret.endswith('+00:00'):
ret = ret[:-6] + 'Z'
return ret
value = value.isoformat()
if value.endswith('+00:00'):
value = value[:-6] + 'Z'
return value
return value.strftime(self.format)

View File

@ -127,7 +127,7 @@ class HyperlinkedRelatedField(RelatedField):
attributes are not configured to correctly match the URL conf.
"""
# Unsaved objects will not yet have a valid URL.
if obj.pk is None:
if obj.pk:
return None
lookup_value = getattr(obj, self.lookup_field)
@ -248,11 +248,13 @@ class ManyRelation(Field):
You shouldn't need to be using this class directly yourself.
"""
initial = []
def __init__(self, child_relation=None, *args, **kwargs):
self.child_relation = child_relation
assert child_relation is not None, '`child_relation` is a required argument.'
super(ManyRelation, self).__init__(*args, **kwargs)
self.child_relation.bind(field_name='', parent=self)
def to_internal_value(self, data):
return [

View File

@ -377,23 +377,21 @@ class HTMLFormRenderer(BaseRenderer):
serializers.TimeField: 'time',
})
def render_field(self, field, value, errors, layout=None):
def render_field(self, field, layout=None):
layout = layout or 'vertical'
style_type = field.style.get('type', 'default')
if style_type == 'textarea' and layout == 'inline':
style_type = 'default'
input_type = self.input_type[field]
if input_type == 'datetime-local':
value = value.rstrip('Z')
if 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 = 'rest_framework/fields/' + layout + '/' + base
template = loader.get_template(template_name)
context = Context({
'field': field,
'value': value,
'errors': errors,
'input_type': input_type
})
@ -408,7 +406,7 @@ class HTMLFormRenderer(BaseRenderer):
template = loader.get_template(self.template)
context = RequestContext(request, {
'form': data,
'form': data.serializer,
'layout': getattr(getattr(data, 'Meta', None), 'layout', 'horizontal'),
'renderer': self
})
@ -479,27 +477,29 @@ class BrowsableAPIRenderer(BaseRenderer):
return False # Doesn't have permissions
return True
def get_rendered_html_form(self, view, method, request):
def get_rendered_html_form(self, data, view, method, request):
"""
Return a string representing a rendered HTML form, possibly bound to
either the input or output data.
In the absence of the View having an associated form then return None.
"""
serializer = getattr(data, 'serializer', None)
if serializer and not getattr(serializer, 'many', False):
instance = getattr(serializer, 'instance', None)
else:
instance = None
if request.method == method:
try:
data = request.data
# files = request.FILES
except ParseError:
data = None
# files = None
else:
data = None
# files = None
with override_method(view, request, method) as request:
obj = getattr(view, 'object', None)
if not self.show_form_for_method(view, method, request, obj):
if not self.show_form_for_method(view, method, request, instance):
return
if method in ('DELETE', 'OPTIONS'):
@ -511,19 +511,24 @@ class BrowsableAPIRenderer(BaseRenderer):
):
return
serializer = view.get_serializer(instance=obj, data=data)
serializer.is_valid()
data = serializer.data
serializer = view.get_serializer(instance=instance, data=data)
if data is not None:
serializer.is_valid()
form_renderer = self.form_renderer_class()
return form_renderer.render(data, self.accepted_media_type, self.renderer_context)
return form_renderer.render(serializer.data, self.accepted_media_type, self.renderer_context)
def get_raw_data_form(self, view, method, request):
def get_raw_data_form(self, data, view, method, request):
"""
Returns a form that allows for arbitrary content types to be tunneled
via standard HTML forms.
(Which are typically application/x-www-form-urlencoded)
"""
serializer = getattr(data, 'serializer', None)
if serializer and not getattr(serializer, 'many', False):
instance = getattr(serializer, 'instance', None)
else:
instance = None
with override_method(view, request, method) as request:
# If we're not using content overloading there's no point in
# supplying a generic form, as the view won't treat the form's
@ -533,8 +538,7 @@ class BrowsableAPIRenderer(BaseRenderer):
return None
# Check permissions
obj = getattr(view, 'object', None)
if not self.show_form_for_method(view, method, request, obj):
if not self.show_form_for_method(view, method, request, instance):
return
# If possible, serialize the initial content for the generic form
@ -545,8 +549,8 @@ class BrowsableAPIRenderer(BaseRenderer):
# corresponding renderer that can be used to render the data.
# Get a read-only version of the serializer
serializer = view.get_serializer(instance=obj)
if obj is None:
serializer = view.get_serializer(instance=instance)
if instance is None:
for name, field in serializer.fields.items():
if getattr(field, 'read_only', None):
del serializer.fields[name]
@ -606,9 +610,9 @@ class BrowsableAPIRenderer(BaseRenderer):
renderer = self.get_default_renderer(view)
raw_data_post_form = self.get_raw_data_form(view, 'POST', request)
raw_data_put_form = self.get_raw_data_form(view, 'PUT', request)
raw_data_patch_form = self.get_raw_data_form(view, 'PATCH', request)
raw_data_post_form = self.get_raw_data_form(data, view, 'POST', request)
raw_data_put_form = self.get_raw_data_form(data, view, 'PUT', request)
raw_data_patch_form = self.get_raw_data_form(data, view, 'PATCH', request)
raw_data_put_or_patch_form = raw_data_put_form or raw_data_patch_form
response_headers = dict(response.items())
@ -632,10 +636,10 @@ class BrowsableAPIRenderer(BaseRenderer):
'available_formats': [renderer_cls.format for renderer_cls in view.renderer_classes],
'response_headers': response_headers,
# 'put_form': self.get_rendered_html_form(view, 'PUT', request),
# 'post_form': self.get_rendered_html_form(view, 'POST', request),
# 'delete_form': self.get_rendered_html_form(view, 'DELETE', request),
# 'options_form': self.get_rendered_html_form(view, 'OPTIONS', request),
'put_form': self.get_rendered_html_form(data, view, 'PUT', request),
'post_form': self.get_rendered_html_form(data, view, 'POST', request),
'delete_form': self.get_rendered_html_form(data, view, 'DELETE', request),
'options_form': self.get_rendered_html_form(data, view, 'OPTIONS', request),
'raw_data_put_form': raw_data_put_form,
'raw_data_post_form': raw_data_post_form,

View File

@ -14,7 +14,6 @@ from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.db import models
from django.utils import six
from django.utils.datastructures import SortedDict
from collections import namedtuple
from rest_framework.fields import empty, set_value, Field, SkipField
from rest_framework.settings import api_settings
from rest_framework.utils import html, model_meta, representation
@ -38,8 +37,8 @@ from rest_framework.relations import * # NOQA
from rest_framework.fields import * # NOQA
FieldResult = namedtuple('FieldResult', ['field', 'value', 'error'])
# BaseSerializer
# --------------
class BaseSerializer(Field):
"""
@ -113,11 +112,6 @@ class BaseSerializer(Field):
if not hasattr(self, '_data'):
if self.instance is not None:
self._data = self.to_representation(self.instance)
elif self._initial_data is not None:
self._data = dict([
(field_name, field.get_value(self._initial_data))
for field_name, field in self.fields.items()
])
else:
self._data = self.get_initial()
return self._data
@ -137,6 +131,79 @@ class BaseSerializer(Field):
return self._validated_data
# Serializer & ListSerializer classes
# -----------------------------------
class ReturnDict(SortedDict):
"""
Return object from `serialier.data` for the `Serializer` class.
Includes a backlink to the serializer instance for renderers
to use if they need richer field information.
"""
def __init__(self, *args, **kwargs):
self.serializer = kwargs.pop('serializer')
super(ReturnDict, self).__init__(*args, **kwargs)
class ReturnList(list):
"""
Return object from `serialier.data` for the `SerializerList` class.
Includes a backlink to the serializer instance for renderers
to use if they need richer field information.
"""
def __init__(self, *args, **kwargs):
self.serializer = kwargs.pop('serializer')
super(ReturnList, self).__init__(*args, **kwargs)
class BoundField(object):
"""
A field object that also includes `.value` and `.error` properties.
Returned when iterating over a serializer instance,
providing an API similar to Django forms and form fields.
"""
def __init__(self, field, value, errors):
self._field = field
self.value = value
self.errors = errors
def __getattr__(self, attr_name):
return getattr(self._field, attr_name)
@property
def _proxy_class(self):
return self._field.__class__
class BindingDict(object):
"""
This dict-like object is used to store fields on a serializer.
This ensures that whenever fields are added to the serializer we call
`field.bind()` so that the `field_name` and `parent` attributes
can be set correctly.
"""
def __init__(self, serializer):
self.serializer = serializer
self.fields = SortedDict()
def __setitem__(self, key, field):
self.fields[key] = field
field.bind(field_name=key, parent=self.serializer)
def __getitem__(self, key):
return self.fields[key]
def __delitem__(self, key):
del self.fields[key]
def items(self):
return self.fields.items()
def values(self):
return self.fields.values()
class SerializerMetaclass(type):
"""
This metaclass sets a dictionary named `base_fields` on the class.
@ -167,35 +234,6 @@ class SerializerMetaclass(type):
return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs)
class BindingDict(object):
"""
This dict-like object is used to store fields on a serializer.
This ensures that whenever fields are added to the serializer we call
`field.bind()` so that the `field_name` and `parent` attributes
can be set correctly.
"""
def __init__(self, serializer):
self.serializer = serializer
self.fields = SortedDict()
def __setitem__(self, key, field):
self.fields[key] = field
field.bind(field_name=key, parent=self.serializer)
def __getitem__(self, key):
return self.fields[key]
def __delitem__(self, key):
del self.fields[key]
def items(self):
return self.fields.items()
def values(self):
return self.fields.values()
@six.add_metaclass(SerializerMetaclass)
class Serializer(BaseSerializer):
def __init__(self, *args, **kwargs):
@ -212,10 +250,18 @@ class Serializer(BaseSerializer):
return copy.deepcopy(self._declared_fields)
def get_initial(self):
return dict([
if self._initial_data is not None:
return ReturnDict([
(field_name, field.get_value(self._initial_data))
for field_name, field in self.fields.items()
], serializer=self)
#return self.to_representation(self._initial_data)
return ReturnDict([
(field.field_name, field.get_initial())
for field in self.fields.values()
])
if not field.write_only
], serializer=self)
def get_value(self, dictionary):
# We override the default field access in order to support
@ -288,7 +334,7 @@ class Serializer(BaseSerializer):
"""
Object instance -> Dict of primitive datatypes.
"""
ret = SortedDict()
ret = ReturnDict(serializer=self)
fields = [field for field in self.fields.values() if not field.write_only]
for field in fields:
@ -302,11 +348,9 @@ class Serializer(BaseSerializer):
def __iter__(self):
errors = self.errors if hasattr(self, '_errors') else {}
for field in self.fields.values():
if field.read_only:
continue
value = self.data.get(field.field_name) if self.data else None
error = errors.get(field.field_name)
yield FieldResult(field, value, error)
yield BoundField(field, value, error)
def __repr__(self):
return representation.serializer_repr(self, indent=1)
@ -317,7 +361,7 @@ class Serializer(BaseSerializer):
class ListSerializer(BaseSerializer):
child = None
initial = []
many = True
def __init__(self, *args, **kwargs):
self.child = kwargs.pop('child', copy.deepcopy(self.child))
@ -326,6 +370,11 @@ class ListSerializer(BaseSerializer):
super(ListSerializer, self).__init__(*args, **kwargs)
self.child.bind(field_name='', parent=self)
def get_initial(self):
if self._initial_data is not None:
return self.to_representation(self._initial_data)
return ReturnList(serializer=self)
def get_value(self, dictionary):
# We override the default field access in order to support
# lists in HTML forms.
@ -345,7 +394,10 @@ class ListSerializer(BaseSerializer):
"""
List of object instances -> List of dicts of primitive datatypes.
"""
return [self.child.to_representation(item) for item in data]
return ReturnList(
[self.child.to_representation(item) for item in data],
serializer=self
)
def create(self, attrs_list):
return [self.child.create(attrs) for attrs in attrs_list]
@ -354,6 +406,9 @@ class ListSerializer(BaseSerializer):
return representation.list_repr(self, indent=1)
# ModelSerializer & HyperlinkedModelSerializer
# --------------------------------------------
class ModelSerializer(Serializer):
_field_mapping = ClassLookupDict({
models.AutoField: IntegerField,

View File

@ -10,6 +10,12 @@ a single block in the template.
background: transparent;
border-top-color: transparent;
padding-top: 0;
text-align: right;
}
#generic-content-form textarea {
font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
font-size: 80%;
}
.navbar-inverse .brand a {
@ -29,7 +35,7 @@ a single block in the template.
z-index: 3;
}
.navbar .navbar-inner {
.navbar {
background: #2C2C2C;
color: white;
border: none;
@ -37,7 +43,7 @@ a single block in the template.
border-radius: 0px;
}
.navbar .navbar-inner .nav li, .navbar .navbar-inner .nav li a, .navbar .navbar-inner .brand:hover {
.navbar .nav li, .navbar .nav li a, .navbar .brand:hover {
color: white;
}
@ -45,11 +51,11 @@ a single block in the template.
background: #2C2C2C;
}
.navbar .navbar-inner .dropdown-menu li a, .navbar .navbar-inner .dropdown-menu li {
.navbar .dropdown-menu li a, .navbar .dropdown-menu li {
color: #A30000;
}
.navbar .navbar-inner .dropdown-menu li a:hover {
.navbar .dropdown-menu li a:hover {
background: #EEEEEE;
color: #C20000;
}
@ -61,10 +67,10 @@ html {
background: none;
}
body, .navbar .navbar-inner .container-fluid {
/*body, .navbar .container-fluid {
max-width: 1150px;
margin: 0 auto;
}
}*/
body {
background: url("../img/grid.png") repeat-x;

View File

@ -15,7 +15,8 @@
{% block style %}
{% block bootstrap_theme %}
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<!--<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/>-->
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap-tweaks.css" %}"/>
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/prettify.css" %}"/>
@ -26,44 +27,42 @@
</head>
{% block body %}
<body class="{% block bodyclass %}{% endblock %} container">
<body class="{% block bodyclass %}{% endblock %}">
<div class="wrapper">
{% block navbar %}
<div class="navbar {% block bootstrap_navbar_variant %}navbar-inverse{% endblock %}">
<div class="navbar-inner">
<div class="container-fluid">
<span>
{% block branding %}
<a class='brand' rel="nofollow" href='http://www.django-rest-framework.org'>
Django REST framework <span class="version">{{ version }}</span>
</a>
{% endblock %}
</span>
<ul class="nav pull-right">
{% block userlinks %}
{% if user.is_authenticated %}
{% optional_logout request user %}
{% else %}
{% optional_login request %}
{% endif %}
{% endblock %}
</ul>
</div>
<div class="navbar navbar-static-top {% block bootstrap_navbar_variant %}navbar-inverse{% endblock %}">
<div class="container">
<span>
{% block branding %}
<a class='navbar-brand' rel="nofollow" href='http://www.django-rest-framework.org'>
Django REST framework <span class="version">{{ version }}</span>
</a>
{% endblock %}
</span>
<ul class="nav navbar-nav pull-right">
{% block userlinks %}
{% if user.is_authenticated %}
{% optional_logout request user %}
{% else %}
{% optional_login request %}
{% endif %}
{% endblock %}
</ul>
</div>
</div>
{% endblock %}
<div class="container">
{% block breadcrumbs %}
<ul class="breadcrumb">
{% for breadcrumb_name, breadcrumb_url in breadcrumblist %}
<li>
<a href="{{ breadcrumb_url }}" {% if forloop.last %}class="active"{% endif %}>
{{ breadcrumb_name }}
</a>
{% if not forloop.last %}<span class="divider">&rsaquo;</span>{% endif %}
</li>
{% if forloop.last %}
<li class="active">{{ breadcrumb_name }}</li>
{% else %}
<li><a href="{{ breadcrumb_url }}">{{ breadcrumb_name }}</a></li>
{% endif %}
{% endfor %}
</ul>
{% endblock %}
@ -238,6 +237,7 @@
{% endif %}
</div>
<!-- END Content -->
</div><!-- /.container -->
<footer>
{% block footer %}

View File

@ -2,7 +2,7 @@
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label>
<input type="checkbox" name="{{ field.field_name }}" value="true" {% if value %}checked{% endif %}>
<input type="checkbox" name="{{ field.field_name }}" value="true" {% if field.value %}checked{% endif %}>
{% if field.label %}{{ field.label }}{% endif %}
</label>
</div>

View File

@ -4,7 +4,7 @@
<legend class="control-label col-sm-2 {% if field.style.hide_label %}sr-only{% endif %}" style="border-bottom: 0">{{ field.label }}</legend>
</div>
{% endif %}
{% for field_item in value.field_items.values() %}
{% for field_item in field.value.field_items.values() %}
{{ renderer.render_field(field_item, layout=layout) }}
{% endfor %}
</fieldset>

View File

@ -1,7 +1,7 @@
<div class="form-group">
{% include "rest_framework/fields/horizontal/label.html" %}
<div class="col-sm-10">
<input type="{{ input_type }}" class="form-control" {% include "rest_framework/fields/attrs.html" %} {% if value %}value="{{ value }}"{% endif %}>
<input type="{{ input_type }}" class="form-control" {% include "rest_framework/fields/attrs.html" %} {% if field.value %}value="{{ field.value }}"{% endif %}>
{% if field.help_text %}<p class="help-block">{{ field.help_text }}</p>{% endif %}
</div>
</div>

View File

@ -3,7 +3,7 @@
<div class="col-sm-10">
<select class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %}
<option value="{{ key }}" {% if key == value %}selected{% endif %}>{{ text }}</option>
<option value="{{ key }}" {% if key == field.value %}selected{% endif %}>{{ text }}</option>
{% endfor %}
</select>
</div>

View File

@ -4,7 +4,7 @@
{% if field.style.inline %}
{% for key, text in field.choices.items %}
<label class="checkbox-inline">
<input type="checkbox" name="{{ field.field_name }}" value="{{ key }}" {% if key in value %}checked{% endif %}>
<input type="checkbox" name="{{ field.field_name }}" value="{{ key }}" {% if key in field.value %}checked{% endif %}>
{{ text }}
</label>
{% endfor %}
@ -12,7 +12,7 @@
{% for key, text in field.choices.items %}
<div class="checkbox">
<label>
<input type="checkbox" name="{{ field.field_name }}" value="{{ key }}" {% if key in value %}checked{% endif %}>
<input type="checkbox" name="{{ field.field_name }}" value="{{ key }}" {% if key in field.value %}checked{% endif %}>
{{ text }}
</label>
</div>

View File

@ -3,7 +3,7 @@
<div class="col-sm-10">
<select multiple class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %}
<option value="{{ key }}" {% if key in value %}selected{% endif %}>{{ text }}</option>
<option value="{{ key }}" {% if key in field.value %}selected{% endif %}>{{ text }}</option>
{% endfor %}
</select>
</div>

View File

@ -4,7 +4,7 @@
{% if field.style.inline %}
{% for key, text in field.choices.items %}
<label class="radio-inline">
<input type="radio" name="{{ field.field_name }}" value="{{ key }}" {% if key == value %}checked{% endif %}>
<input type="radio" name="{{ field.field_name }}" value="{{ key }}" {% if key == field.value %}checked{% endif %}>
{{ text }}
</label>
{% endfor %}
@ -12,7 +12,7 @@
{% for key, text in field.choices.items %}
<div class="radio">
<label>
<input type="radio" name="{{ field.field_name }}" value="{{ key }}" {% if key == value %}checked{% endif %}>
<input type="radio" name="{{ field.field_name }}" value="{{ key }}" {% if key == field.value %}checked{% endif %}>
{{ text }}
</label>
</div>

View File

@ -1,7 +1,7 @@
<div class="form-group">
{% include "rest_framework/fields/horizontal/label.html" %}
<div class="col-sm-10">
<textarea class="form-control" {% include "rest_framework/fields/attrs.html" %}>{% if value %}{{ value }}{% endif %}</textarea>
<textarea class="form-control" {% include "rest_framework/fields/attrs.html" %}>{% if field.value %}{{ field.value }}{% endif %}</textarea>
{% if field.help_text %}<p class="help-block">{{ field.help_text }}</p>{% endif %}
</div>
</div>

View File

@ -1,6 +1,6 @@
<div class="checkbox">
<label>
<input type="checkbox" name="{{ field.field_name }}" value="true" {% if value %}checked{% endif %}>
<input type="checkbox" name="{{ field.field_name }}" value="true" {% if field.value %}checked{% endif %}>
{% if field.label %}{{ field.label }}{% endif %}
</label>
</div>

View File

@ -1,3 +1,3 @@
{% for field_item in value.field_items.values() %}
{% for field_item in field.value.field_items.values() %}
{{ renderer.render_field(field_item, layout=layout) }}
{% endfor %}

View File

@ -1,4 +1,4 @@
<div class="form-group">
{% include "rest_framework/fields/inline/label.html" %}
<input type="{{ input_type }}" class="form-control" {% include "rest_framework/fields/attrs.html" %} {% if value %}value="{{ value }}"{% endif %}>
<input type="{{ input_type }}" class="form-control" {% include "rest_framework/fields/attrs.html" %} {% if field.value %}value="{{ field.value }}"{% endif %}>
</div>

View File

@ -2,7 +2,7 @@
{% include "rest_framework/fields/inline/label.html" %}
<select class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %}
<option value="{{ key }}" {% if key == value %}selected{% endif %}>{{ text }}</option>
<option value="{{ key }}" {% if key == field.value %}selected{% endif %}>{{ text }}</option>
{% endfor %}
</select>
</div>

View File

@ -3,7 +3,7 @@
{% for key, text in field.choices.items %}
<div class="checkbox">
<label>
<input type="checkbox" name="{{ rest_framework/field.field_name }}" value="{{ key }}" {% if key in value %}checked{% endif %}>
<input type="checkbox" name="{{ rest_framework/field.field_name }}" value="{{ key }}" {% if key in field.value %}checked{% endif %}>
{{ text }}
</label>
</div>

View File

@ -2,7 +2,7 @@
{% include "rest_framework/fields/inline/label.html" %}
<select multiple class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %}
<option value="{{ key }}" {% if key in value %}selected{% endif %}>{{ text }}</option>
<option value="{{ key }}" {% if key in field.value %}selected{% endif %}>{{ text }}</option>
{% endfor %}
</select>
</div>

View File

@ -3,7 +3,7 @@
{% for key, text in field.choices.items %}
<div class="radio">
<label>
<input type="radio" name="{{ field.field_name }}" value="{{ key }}" {% if key == value %}checked{% endif %}>
<input type="radio" name="{{ field.field_name }}" value="{{ key }}" {% if key == field.value %}checked{% endif %}>
{{ text }}
</label>
</div>

View File

@ -1,4 +1,4 @@
<div class="form-group">
{% include "rest_framework/fields/inline/label.html" %}
<textarea class="form-control" {% include "rest_framework/fields/attrs.html" %}>{% if value %}{{ value }}{% endif %}</textarea>
<textarea class="form-control" {% include "rest_framework/fields/attrs.html" %}>{% if field.value %}{{ field.value }}{% endif %}</textarea>
</div>

View File

@ -1,6 +1,6 @@
<fieldset>
{% if field.label %}<legend {% if field.style.hide_label %}class="sr-only"{% endif %}>{{ field.label }}</legend>{% endif %}
{% for field_item in value.field_items.values() %}
{% for field_item in field.value.field_items.values() %}
{{ renderer.render_field(field_item, layout=layout) }}
{% endfor %}
</fieldset>

View File

@ -1,5 +1,5 @@
<div class="form-group">
{% include "rest_framework/fields/vertical/label.html" %}
<input type="{{ input_type }}" class="form-control" {% include "rest_framework/fields/attrs.html" %} {% if value %}value="{{ value }}"{% endif %}>
<input type="{{ input_type }}" class="form-control" {% include "rest_framework/fields/attrs.html" %} {% if field.value %}value="{{ field.value }}"{% endif %}>
{% if field.help_text %}<p class="help-block">{{ field.help_text }}</p>{% endif %}
</div>

View File

@ -2,7 +2,7 @@
{% include "rest_framework/fields/vertical/label.html" %}
<select class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %}
<option value="{{ key }}" {% if key == value %}selected{% endif %}>{{ text }}</option>
<option value="{{ key }}" {% if key == field.value %}selected{% endif %}>{{ text }}</option>
{% endfor %}
</select>
</div>

View File

@ -4,7 +4,7 @@
<div>
{% for key, text in field.choices.items %}
<label class="checkbox-inline">
<input type="checkbox" name="{{ field.field_name }}" value="{{ key }}" {% if key in value %}checked{% endif %}>
<input type="checkbox" name="{{ field.field_name }}" value="{{ key }}" {% if key in field.value %}checked{% endif %}>
{{ text }}
</label>
{% endfor %}
@ -13,7 +13,7 @@
{% for key, text in field.choices.items %}
<div class="checkbox">
<label>
<input type="checkbox" name="{{ field.field_name }}" value="{{ key }}" {% if key in value %}checked{% endif %}>
<input type="checkbox" name="{{ field.field_name }}" value="{{ key }}" {% if key in field.value %}checked{% endif %}>
{{ text }}
</label>
</div>

View File

@ -2,7 +2,7 @@
{% include "rest_framework/fields/vertical/label.html" %}
<select multiple class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %}
<option value="{{ key }}" {% if key in value %}selected{% endif %}>{{ text }}</option>
<option value="{{ key }}" {% if key in field.value %}selected{% endif %}>{{ text }}</option>
{% endfor %}
</select>
</div>

View File

@ -4,7 +4,7 @@
<div>
{% for key, text in field.choices.items %}
<label class="radio-inline">
<input type="radio" name="{{ field.field_name }}" value="{{ key }}" {% if key|string==value|string %}checked{% endif %}>
<input type="radio" name="{{ field.field_name }}" value="{{ key }}" {% if key == field.value %}checked{% endif %}>
{{ text }}
</label>
{% endfor %}
@ -13,7 +13,7 @@
{% for key, text in field.choices.items %}
<div class="radio">
<label>
<input type="radio" name="{{ field.field_name }}" value="{{ key }}" {% if key|string==value|string %}checked{% endif %}>
<input type="radio" name="{{ field.field_name }}" value="{{ key }}" {% if key == field.value %}checked{% endif %}>
{{ text }}
</label>
</div>

View File

@ -1,5 +1,5 @@
<div class="form-group">
{% include "rest_framework/fields/vertical/label.html" %}
<textarea class="form-control" {% include "rest_framework/fields/attrs.html" %}>{% if value %}{{ value }}{% endif %}</textarea>
<textarea class="form-control" {% include "rest_framework/fields/attrs.html" %}>{% if field.value %}{{ field.value }}{% endif %}</textarea>
{% if field.help_text %}<p class="help-block">{{ field.help_text }}</p>{% endif %}
</div>

View File

@ -1,4 +1,4 @@
<html>
<!-- <html>
<head>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
</head>
@ -6,13 +6,15 @@
<div class="container">
<h1>User update</h1>
<div class="well">
<div class="well"> -->
{% load rest_framework %}
<form {% if layout == "inline" %}class="form-inline"{% elif layout == "horizontal" %}class="form-horizontal"{% endif %} role="form" action="." method="POST">
{% csrf_token %}
{% for field, value, errors in form %}
{% render_field field value errors layout=layout renderer=renderer %}
{% for field in form %}
{% if not field.read_only %}
{% render_field field layout=layout renderer=renderer %}
{% endif %}
{% endfor %}
<!-- form.non_field_errors -->
{% if layout == "horizontal" %}
@ -25,7 +27,7 @@
<button type="submit" class="btn btn-default">Submit</button>
{% endif %}
</form>
<!--
</div>
</div></body>
</html>
</html> -->

View File

@ -31,12 +31,9 @@ class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])')
# And the template tags themselves...
# @register.simple_tag
# def render_field(field, value, errors, renderer):
# return renderer.render_field(field, value, errors)
@register.simple_tag
def render_field(field, value, errors, layout=None, renderer=None):
return renderer.render_field(field, value, errors, layout)
def render_field(field, layout=None, renderer=None):
return renderer.render_field(field, layout)
@register.simple_tag

View File

@ -21,7 +21,14 @@ class ClassLookupDict(object):
self.mapping = mapping
def __getitem__(self, key):
for cls in inspect.getmro(key.__class__):
if hasattr(key, '_proxy_class'):
# Deal with proxy classes. Ie. BoundField behaves as if it
# is a Field instance when using ClassLookupDict.
base_class = key._proxy_class
else:
base_class = key.__class__
for cls in inspect.getmro(base_class):
if cls in self.mapping:
return self.mapping[cls]
raise KeyError('Class %s not found in lookup.', cls.__name__)