aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2014-10-02 16:24:24 +0100
committerTom Christie2014-10-02 16:24:24 +0100
commitdf7b6fcf58417fd95e49655eb140b387899b1ceb (patch)
treea4a7d932bb3ef7c8e326b0248662fd31edcc2658
parentffc6aa3abcb0f823b43b63db1666913565e6f934 (diff)
downloaddjango-rest-framework-df7b6fcf58417fd95e49655eb140b387899b1ceb.tar.bz2
First pass on incorperating the form rendering into the browsable API
-rw-r--r--rest_framework/fields.py8
-rw-r--r--rest_framework/relations.py4
-rw-r--r--rest_framework/renderers.py62
-rw-r--r--rest_framework/serializers.py129
-rw-r--r--rest_framework/static/rest_framework/css/bootstrap-tweaks.css18
-rw-r--r--rest_framework/templates/rest_framework/base.html56
-rw-r--r--rest_framework/templates/rest_framework/fields/horizontal/checkbox.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/horizontal/fieldset.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/horizontal/input.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/horizontal/select.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/horizontal/select_checkbox.html4
-rw-r--r--rest_framework/templates/rest_framework/fields/horizontal/select_multiple.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/horizontal/select_radio.html4
-rw-r--r--rest_framework/templates/rest_framework/fields/horizontal/textarea.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/inline/checkbox.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/inline/fieldset.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/inline/input.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/inline/select.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/inline/select_checkbox.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/inline/select_multiple.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/inline/select_radio.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/inline/textarea.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/vertical/fieldset.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/vertical/input.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/vertical/select.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/vertical/select_checkbox.html4
-rw-r--r--rest_framework/templates/rest_framework/fields/vertical/select_multiple.html2
-rw-r--r--rest_framework/templates/rest_framework/fields/vertical/select_radio.html4
-rw-r--r--rest_framework/templates/rest_framework/fields/vertical/textarea.html2
-rw-r--r--rest_framework/templates/rest_framework/form.html14
-rw-r--r--rest_framework/templatetags/rest_framework.py7
-rw-r--r--rest_framework/utils/field_mapping.py9
32 files changed, 217 insertions, 144 deletions
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index f3ff2233..c794963e 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -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)
diff --git a/rest_framework/relations.py b/rest_framework/relations.py
index 8c135672..988b9ede 100644
--- a/rest_framework/relations.py
+++ b/rest_framework/relations.py
@@ -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 [
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index 297c60d8..931dd434 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -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,
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 5da81247..0f24ed40 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -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,34 +131,48 @@ class BaseSerializer(Field):
return self._validated_data
-class SerializerMetaclass(type):
+# Serializer & ListSerializer classes
+# -----------------------------------
+
+class ReturnDict(SortedDict):
"""
- This metaclass sets a dictionary named `base_fields` on the class.
+ 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)
- Any instances of `Field` included as attributes on either the class
- or on any of its superclasses will be include in the
- `base_fields` dictionary.
+
+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)
- @classmethod
- def _get_declared_fields(cls, bases, attrs):
- fields = [(field_name, attrs.pop(field_name))
- for field_name, obj in list(attrs.items())
- if isinstance(obj, Field)]
- fields.sort(key=lambda x: x[1]._creation_counter)
- # If this class is subclassing another Serializer, add that Serializer's
- # fields. Note that we loop over the bases in *reverse*. This is necessary
- # in order to maintain the correct order of fields.
- for base in bases[::-1]:
- if hasattr(base, '_declared_fields'):
- fields = list(base._declared_fields.items()) + fields
+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
- return SortedDict(fields)
+ def __getattr__(self, attr_name):
+ return getattr(self._field, attr_name)
- def __new__(cls, name, bases, attrs):
- attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
- return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs)
+ @property
+ def _proxy_class(self):
+ return self._field.__class__
class BindingDict(object):
@@ -196,6 +204,36 @@ class BindingDict(object):
return self.fields.values()
+class SerializerMetaclass(type):
+ """
+ This metaclass sets a dictionary named `base_fields` on the class.
+
+ Any instances of `Field` included as attributes on either the class
+ or on any of its superclasses will be include in the
+ `base_fields` dictionary.
+ """
+
+ @classmethod
+ def _get_declared_fields(cls, bases, attrs):
+ fields = [(field_name, attrs.pop(field_name))
+ for field_name, obj in list(attrs.items())
+ if isinstance(obj, Field)]
+ fields.sort(key=lambda x: x[1]._creation_counter)
+
+ # If this class is subclassing another Serializer, add that Serializer's
+ # fields. Note that we loop over the bases in *reverse*. This is necessary
+ # in order to maintain the correct order of fields.
+ for base in bases[::-1]:
+ if hasattr(base, '_declared_fields'):
+ fields = list(base._declared_fields.items()) + fields
+
+ return SortedDict(fields)
+
+ def __new__(cls, name, bases, attrs):
+ attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
+ return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs)
+
+
@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,
diff --git a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css
index 6fa1e6cb..84389b1d 100644
--- a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css
+++ b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css
@@ -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;
diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html
index a84ccf26..2e03dd98 100644
--- a/rest_framework/templates/rest_framework/base.html
+++ b/rest_framework/templates/rest_framework/base.html
@@ -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 %}
diff --git a/rest_framework/templates/rest_framework/fields/horizontal/checkbox.html b/rest_framework/templates/rest_framework/fields/horizontal/checkbox.html
index dce4a5cf..dd3c3cef 100644
--- a/rest_framework/templates/rest_framework/fields/horizontal/checkbox.html
+++ b/rest_framework/templates/rest_framework/fields/horizontal/checkbox.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/horizontal/fieldset.html b/rest_framework/templates/rest_framework/fields/horizontal/fieldset.html
index 86417633..843a56b2 100644
--- a/rest_framework/templates/rest_framework/fields/horizontal/fieldset.html
+++ b/rest_framework/templates/rest_framework/fields/horizontal/fieldset.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/horizontal/input.html b/rest_framework/templates/rest_framework/fields/horizontal/input.html
index 310154bb..6f1a504b 100644
--- a/rest_framework/templates/rest_framework/fields/horizontal/input.html
+++ b/rest_framework/templates/rest_framework/fields/horizontal/input.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/horizontal/select.html b/rest_framework/templates/rest_framework/fields/horizontal/select.html
index 3f8cab0a..7367d726 100644
--- a/rest_framework/templates/rest_framework/fields/horizontal/select.html
+++ b/rest_framework/templates/rest_framework/fields/horizontal/select.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/horizontal/select_checkbox.html b/rest_framework/templates/rest_framework/fields/horizontal/select_checkbox.html
index 659eede8..381cda2c 100644
--- a/rest_framework/templates/rest_framework/fields/horizontal/select_checkbox.html
+++ b/rest_framework/templates/rest_framework/fields/horizontal/select_checkbox.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/horizontal/select_multiple.html b/rest_framework/templates/rest_framework/fields/horizontal/select_multiple.html
index da25eb2b..29ba8661 100644
--- a/rest_framework/templates/rest_framework/fields/horizontal/select_multiple.html
+++ b/rest_framework/templates/rest_framework/fields/horizontal/select_multiple.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/horizontal/select_radio.html b/rest_framework/templates/rest_framework/fields/horizontal/select_radio.html
index 188f05e2..20aab8b2 100644
--- a/rest_framework/templates/rest_framework/fields/horizontal/select_radio.html
+++ b/rest_framework/templates/rest_framework/fields/horizontal/select_radio.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/horizontal/textarea.html b/rest_framework/templates/rest_framework/fields/horizontal/textarea.html
index e99266f3..3d016195 100644
--- a/rest_framework/templates/rest_framework/fields/horizontal/textarea.html
+++ b/rest_framework/templates/rest_framework/fields/horizontal/textarea.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/inline/checkbox.html b/rest_framework/templates/rest_framework/fields/inline/checkbox.html
index 01d30aae..289bbb4d 100644
--- a/rest_framework/templates/rest_framework/fields/inline/checkbox.html
+++ b/rest_framework/templates/rest_framework/fields/inline/checkbox.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/inline/fieldset.html b/rest_framework/templates/rest_framework/fields/inline/fieldset.html
index d22982fd..380d4627 100644
--- a/rest_framework/templates/rest_framework/fields/inline/fieldset.html
+++ b/rest_framework/templates/rest_framework/fields/inline/fieldset.html
@@ -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 %}
diff --git a/rest_framework/templates/rest_framework/fields/inline/input.html b/rest_framework/templates/rest_framework/fields/inline/input.html
index aefd1672..e4a92ccd 100644
--- a/rest_framework/templates/rest_framework/fields/inline/input.html
+++ b/rest_framework/templates/rest_framework/fields/inline/input.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/inline/select.html b/rest_framework/templates/rest_framework/fields/inline/select.html
index cb9a7013..9f361c4a 100644
--- a/rest_framework/templates/rest_framework/fields/inline/select.html
+++ b/rest_framework/templates/rest_framework/fields/inline/select.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/inline/select_checkbox.html b/rest_framework/templates/rest_framework/fields/inline/select_checkbox.html
index 424df93e..0f33fb69 100644
--- a/rest_framework/templates/rest_framework/fields/inline/select_checkbox.html
+++ b/rest_framework/templates/rest_framework/fields/inline/select_checkbox.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/inline/select_multiple.html b/rest_framework/templates/rest_framework/fields/inline/select_multiple.html
index 6fdfd672..7c9e5168 100644
--- a/rest_framework/templates/rest_framework/fields/inline/select_multiple.html
+++ b/rest_framework/templates/rest_framework/fields/inline/select_multiple.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/inline/select_radio.html b/rest_framework/templates/rest_framework/fields/inline/select_radio.html
index ddabc9e9..177c0eeb 100644
--- a/rest_framework/templates/rest_framework/fields/inline/select_radio.html
+++ b/rest_framework/templates/rest_framework/fields/inline/select_radio.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/inline/textarea.html b/rest_framework/templates/rest_framework/fields/inline/textarea.html
index 31366809..8259487b 100644
--- a/rest_framework/templates/rest_framework/fields/inline/textarea.html
+++ b/rest_framework/templates/rest_framework/fields/inline/textarea.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/vertical/fieldset.html b/rest_framework/templates/rest_framework/fields/vertical/fieldset.html
index cad32df9..8708916b 100644
--- a/rest_framework/templates/rest_framework/fields/vertical/fieldset.html
+++ b/rest_framework/templates/rest_framework/fields/vertical/fieldset.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/vertical/input.html b/rest_framework/templates/rest_framework/fields/vertical/input.html
index c25407d1..3ee2716a 100644
--- a/rest_framework/templates/rest_framework/fields/vertical/input.html
+++ b/rest_framework/templates/rest_framework/fields/vertical/input.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/vertical/select.html b/rest_framework/templates/rest_framework/fields/vertical/select.html
index 44679d8a..dcc9a3cd 100644
--- a/rest_framework/templates/rest_framework/fields/vertical/select.html
+++ b/rest_framework/templates/rest_framework/fields/vertical/select.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/vertical/select_checkbox.html b/rest_framework/templates/rest_framework/fields/vertical/select_checkbox.html
index e60574c0..1fbe6a94 100644
--- a/rest_framework/templates/rest_framework/fields/vertical/select_checkbox.html
+++ b/rest_framework/templates/rest_framework/fields/vertical/select_checkbox.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/vertical/select_multiple.html b/rest_framework/templates/rest_framework/fields/vertical/select_multiple.html
index 00b25b4b..2cc40d99 100644
--- a/rest_framework/templates/rest_framework/fields/vertical/select_multiple.html
+++ b/rest_framework/templates/rest_framework/fields/vertical/select_multiple.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/vertical/select_radio.html b/rest_framework/templates/rest_framework/fields/vertical/select_radio.html
index 4ffe38ea..470bcb0b 100644
--- a/rest_framework/templates/rest_framework/fields/vertical/select_radio.html
+++ b/rest_framework/templates/rest_framework/fields/vertical/select_radio.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/fields/vertical/textarea.html b/rest_framework/templates/rest_framework/fields/vertical/textarea.html
index 33cb27c7..406cfa77 100644
--- a/rest_framework/templates/rest_framework/fields/vertical/textarea.html
+++ b/rest_framework/templates/rest_framework/fields/vertical/textarea.html
@@ -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>
diff --git a/rest_framework/templates/rest_framework/form.html b/rest_framework/templates/rest_framework/form.html
index 658aa293..162c5633 100644
--- a/rest_framework/templates/rest_framework/form.html
+++ b/rest_framework/templates/rest_framework/form.html
@@ -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> -->
diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py
index 88ff9d4e..49a4c338 100644
--- a/rest_framework/templatetags/rest_framework.py
+++ b/rest_framework/templatetags/rest_framework.py
@@ -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
diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py
index b4d33e39..30fae370 100644
--- a/rest_framework/utils/field_mapping.py
+++ b/rest_framework/utils/field_mapping.py
@@ -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__)