Commit df7b6fcf by Tom Christie

First pass on incorperating the form rendering into the browsable API

parent ffc6aa3a
...@@ -689,10 +689,10 @@ class DateTimeField(Field): ...@@ -689,10 +689,10 @@ class DateTimeField(Field):
return value return value
if self.format.lower() == ISO_8601: if self.format.lower() == ISO_8601:
ret = value.isoformat() value = value.isoformat()
if ret.endswith('+00:00'): if value.endswith('+00:00'):
ret = ret[:-6] + 'Z' value = value[:-6] + 'Z'
return ret return value
return value.strftime(self.format) return value.strftime(self.format)
......
...@@ -127,7 +127,7 @@ class HyperlinkedRelatedField(RelatedField): ...@@ -127,7 +127,7 @@ class HyperlinkedRelatedField(RelatedField):
attributes are not configured to correctly match the URL conf. attributes are not configured to correctly match the URL conf.
""" """
# Unsaved objects will not yet have a valid URL. # Unsaved objects will not yet have a valid URL.
if obj.pk is None: if obj.pk:
return None return None
lookup_value = getattr(obj, self.lookup_field) lookup_value = getattr(obj, self.lookup_field)
...@@ -248,11 +248,13 @@ class ManyRelation(Field): ...@@ -248,11 +248,13 @@ class ManyRelation(Field):
You shouldn't need to be using this class directly yourself. You shouldn't need to be using this class directly yourself.
""" """
initial = []
def __init__(self, child_relation=None, *args, **kwargs): def __init__(self, child_relation=None, *args, **kwargs):
self.child_relation = child_relation self.child_relation = child_relation
assert child_relation is not None, '`child_relation` is a required argument.' assert child_relation is not None, '`child_relation` is a required argument.'
super(ManyRelation, self).__init__(*args, **kwargs) super(ManyRelation, self).__init__(*args, **kwargs)
self.child_relation.bind(field_name='', parent=self)
def to_internal_value(self, data): def to_internal_value(self, data):
return [ return [
......
...@@ -377,23 +377,21 @@ class HTMLFormRenderer(BaseRenderer): ...@@ -377,23 +377,21 @@ class HTMLFormRenderer(BaseRenderer):
serializers.TimeField: 'time', serializers.TimeField: 'time',
}) })
def render_field(self, field, value, errors, layout=None): def render_field(self, field, layout=None):
layout = layout or 'vertical' layout = layout or 'vertical'
style_type = field.style.get('type', 'default') style_type = field.style.get('type', 'default')
if style_type == 'textarea' and layout == 'inline': if style_type == 'textarea' and layout == 'inline':
style_type = 'default' style_type = 'default'
input_type = self.input_type[field] input_type = self.input_type[field]
if input_type == 'datetime-local': if input_type == 'datetime-local' and isinstance(field.value, six.text_type):
value = value.rstrip('Z') field.value = field.value.rstrip('Z')
base = self.field_templates[field][style_type] base = self.field_templates[field][style_type]
template_name = 'rest_framework/fields/' + layout + '/' + base template_name = 'rest_framework/fields/' + layout + '/' + base
template = loader.get_template(template_name) template = loader.get_template(template_name)
context = Context({ context = Context({
'field': field, 'field': field,
'value': value,
'errors': errors,
'input_type': input_type 'input_type': input_type
}) })
...@@ -408,7 +406,7 @@ class HTMLFormRenderer(BaseRenderer): ...@@ -408,7 +406,7 @@ class HTMLFormRenderer(BaseRenderer):
template = loader.get_template(self.template) template = loader.get_template(self.template)
context = RequestContext(request, { context = RequestContext(request, {
'form': data, 'form': data.serializer,
'layout': getattr(getattr(data, 'Meta', None), 'layout', 'horizontal'), 'layout': getattr(getattr(data, 'Meta', None), 'layout', 'horizontal'),
'renderer': self 'renderer': self
}) })
...@@ -479,27 +477,29 @@ class BrowsableAPIRenderer(BaseRenderer): ...@@ -479,27 +477,29 @@ class BrowsableAPIRenderer(BaseRenderer):
return False # Doesn't have permissions return False # Doesn't have permissions
return True 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 Return a string representing a rendered HTML form, possibly bound to
either the input or output data. either the input or output data.
In the absence of the View having an associated form then return None. 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: if request.method == method:
try: try:
data = request.data data = request.data
# files = request.FILES
except ParseError: except ParseError:
data = None data = None
# files = None
else: else:
data = None data = None
# files = None
with override_method(view, request, method) as request: with override_method(view, request, method) as request:
obj = getattr(view, 'object', None) if not self.show_form_for_method(view, method, request, instance):
if not self.show_form_for_method(view, method, request, obj):
return return
if method in ('DELETE', 'OPTIONS'): if method in ('DELETE', 'OPTIONS'):
...@@ -511,19 +511,24 @@ class BrowsableAPIRenderer(BaseRenderer): ...@@ -511,19 +511,24 @@ class BrowsableAPIRenderer(BaseRenderer):
): ):
return return
serializer = view.get_serializer(instance=obj, data=data) serializer = view.get_serializer(instance=instance, data=data)
if data is not None:
serializer.is_valid() serializer.is_valid()
data = serializer.data
form_renderer = self.form_renderer_class() 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 Returns a form that allows for arbitrary content types to be tunneled
via standard HTML forms. via standard HTML forms.
(Which are typically application/x-www-form-urlencoded) (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: with override_method(view, request, method) as request:
# If we're not using content overloading there's no point in # 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 # supplying a generic form, as the view won't treat the form's
...@@ -533,8 +538,7 @@ class BrowsableAPIRenderer(BaseRenderer): ...@@ -533,8 +538,7 @@ class BrowsableAPIRenderer(BaseRenderer):
return None return None
# Check permissions # Check permissions
obj = getattr(view, 'object', None) if not self.show_form_for_method(view, method, request, instance):
if not self.show_form_for_method(view, method, request, obj):
return return
# If possible, serialize the initial content for the generic form # If possible, serialize the initial content for the generic form
...@@ -545,8 +549,8 @@ class BrowsableAPIRenderer(BaseRenderer): ...@@ -545,8 +549,8 @@ class BrowsableAPIRenderer(BaseRenderer):
# corresponding renderer that can be used to render the data. # corresponding renderer that can be used to render the data.
# Get a read-only version of the serializer # Get a read-only version of the serializer
serializer = view.get_serializer(instance=obj) serializer = view.get_serializer(instance=instance)
if obj is None: if instance is None:
for name, field in serializer.fields.items(): for name, field in serializer.fields.items():
if getattr(field, 'read_only', None): if getattr(field, 'read_only', None):
del serializer.fields[name] del serializer.fields[name]
...@@ -606,9 +610,9 @@ class BrowsableAPIRenderer(BaseRenderer): ...@@ -606,9 +610,9 @@ class BrowsableAPIRenderer(BaseRenderer):
renderer = self.get_default_renderer(view) renderer = self.get_default_renderer(view)
raw_data_post_form = self.get_raw_data_form(view, 'POST', request) raw_data_post_form = self.get_raw_data_form(data, view, 'POST', request)
raw_data_put_form = self.get_raw_data_form(view, 'PUT', request) raw_data_put_form = self.get_raw_data_form(data, view, 'PUT', request)
raw_data_patch_form = self.get_raw_data_form(view, 'PATCH', 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 raw_data_put_or_patch_form = raw_data_put_form or raw_data_patch_form
response_headers = dict(response.items()) response_headers = dict(response.items())
...@@ -632,10 +636,10 @@ class BrowsableAPIRenderer(BaseRenderer): ...@@ -632,10 +636,10 @@ class BrowsableAPIRenderer(BaseRenderer):
'available_formats': [renderer_cls.format for renderer_cls in view.renderer_classes], 'available_formats': [renderer_cls.format for renderer_cls in view.renderer_classes],
'response_headers': response_headers, 'response_headers': response_headers,
# 'put_form': self.get_rendered_html_form(view, 'PUT', request), 'put_form': self.get_rendered_html_form(data, view, 'PUT', request),
# 'post_form': self.get_rendered_html_form(view, 'POST', request), 'post_form': self.get_rendered_html_form(data, view, 'POST', request),
# 'delete_form': self.get_rendered_html_form(view, 'DELETE', request), 'delete_form': self.get_rendered_html_form(data, view, 'DELETE', request),
# 'options_form': self.get_rendered_html_form(view, 'OPTIONS', request), 'options_form': self.get_rendered_html_form(data, view, 'OPTIONS', request),
'raw_data_put_form': raw_data_put_form, 'raw_data_put_form': raw_data_put_form,
'raw_data_post_form': raw_data_post_form, 'raw_data_post_form': raw_data_post_form,
......
...@@ -14,7 +14,6 @@ from django.core.exceptions import ImproperlyConfigured, ValidationError ...@@ -14,7 +14,6 @@ from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.db import models from django.db import models
from django.utils import six from django.utils import six
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from collections import namedtuple
from rest_framework.fields import empty, set_value, Field, SkipField from rest_framework.fields import empty, set_value, Field, SkipField
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.utils import html, model_meta, representation from rest_framework.utils import html, model_meta, representation
...@@ -38,8 +37,8 @@ from rest_framework.relations import * # NOQA ...@@ -38,8 +37,8 @@ from rest_framework.relations import * # NOQA
from rest_framework.fields import * # NOQA from rest_framework.fields import * # NOQA
FieldResult = namedtuple('FieldResult', ['field', 'value', 'error']) # BaseSerializer
# --------------
class BaseSerializer(Field): class BaseSerializer(Field):
""" """
...@@ -113,11 +112,6 @@ class BaseSerializer(Field): ...@@ -113,11 +112,6 @@ class BaseSerializer(Field):
if not hasattr(self, '_data'): if not hasattr(self, '_data'):
if self.instance is not None: if self.instance is not None:
self._data = self.to_representation(self.instance) 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: else:
self._data = self.get_initial() self._data = self.get_initial()
return self._data return self._data
...@@ -137,34 +131,48 @@ class BaseSerializer(Field): ...@@ -137,34 +131,48 @@ class BaseSerializer(Field):
return self._validated_data 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 class ReturnList(list):
`base_fields` dictionary.
""" """
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 class BoundField(object):
# fields. Note that we loop over the bases in *reverse*. This is necessary """
# in order to maintain the correct order of fields. A field object that also includes `.value` and `.error` properties.
for base in bases[::-1]: Returned when iterating over a serializer instance,
if hasattr(base, '_declared_fields'): providing an API similar to Django forms and form fields.
fields = list(base._declared_fields.items()) + 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): @property
attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs) def _proxy_class(self):
return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs) return self._field.__class__
class BindingDict(object): class BindingDict(object):
...@@ -196,6 +204,36 @@ class BindingDict(object): ...@@ -196,6 +204,36 @@ class BindingDict(object):
return self.fields.values() 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) @six.add_metaclass(SerializerMetaclass)
class Serializer(BaseSerializer): class Serializer(BaseSerializer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
...@@ -212,10 +250,18 @@ class Serializer(BaseSerializer): ...@@ -212,10 +250,18 @@ class Serializer(BaseSerializer):
return copy.deepcopy(self._declared_fields) return copy.deepcopy(self._declared_fields)
def get_initial(self): 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()) (field.field_name, field.get_initial())
for field in self.fields.values() for field in self.fields.values()
]) if not field.write_only
], serializer=self)
def get_value(self, dictionary): def get_value(self, dictionary):
# We override the default field access in order to support # We override the default field access in order to support
...@@ -288,7 +334,7 @@ class Serializer(BaseSerializer): ...@@ -288,7 +334,7 @@ class Serializer(BaseSerializer):
""" """
Object instance -> Dict of primitive datatypes. 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] fields = [field for field in self.fields.values() if not field.write_only]
for field in fields: for field in fields:
...@@ -302,11 +348,9 @@ class Serializer(BaseSerializer): ...@@ -302,11 +348,9 @@ class Serializer(BaseSerializer):
def __iter__(self): def __iter__(self):
errors = self.errors if hasattr(self, '_errors') else {} errors = self.errors if hasattr(self, '_errors') else {}
for field in self.fields.values(): for field in self.fields.values():
if field.read_only:
continue
value = self.data.get(field.field_name) if self.data else None value = self.data.get(field.field_name) if self.data else None
error = errors.get(field.field_name) error = errors.get(field.field_name)
yield FieldResult(field, value, error) yield BoundField(field, value, error)
def __repr__(self): def __repr__(self):
return representation.serializer_repr(self, indent=1) return representation.serializer_repr(self, indent=1)
...@@ -317,7 +361,7 @@ class Serializer(BaseSerializer): ...@@ -317,7 +361,7 @@ class Serializer(BaseSerializer):
class ListSerializer(BaseSerializer): class ListSerializer(BaseSerializer):
child = None child = None
initial = [] many = True
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.child = kwargs.pop('child', copy.deepcopy(self.child)) self.child = kwargs.pop('child', copy.deepcopy(self.child))
...@@ -326,6 +370,11 @@ class ListSerializer(BaseSerializer): ...@@ -326,6 +370,11 @@ class ListSerializer(BaseSerializer):
super(ListSerializer, self).__init__(*args, **kwargs) super(ListSerializer, self).__init__(*args, **kwargs)
self.child.bind(field_name='', parent=self) 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): def get_value(self, dictionary):
# We override the default field access in order to support # We override the default field access in order to support
# lists in HTML forms. # lists in HTML forms.
...@@ -345,7 +394,10 @@ class ListSerializer(BaseSerializer): ...@@ -345,7 +394,10 @@ class ListSerializer(BaseSerializer):
""" """
List of object instances -> List of dicts of primitive datatypes. 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): def create(self, attrs_list):
return [self.child.create(attrs) for attrs in attrs_list] return [self.child.create(attrs) for attrs in attrs_list]
...@@ -354,6 +406,9 @@ class ListSerializer(BaseSerializer): ...@@ -354,6 +406,9 @@ class ListSerializer(BaseSerializer):
return representation.list_repr(self, indent=1) return representation.list_repr(self, indent=1)
# ModelSerializer & HyperlinkedModelSerializer
# --------------------------------------------
class ModelSerializer(Serializer): class ModelSerializer(Serializer):
_field_mapping = ClassLookupDict({ _field_mapping = ClassLookupDict({
models.AutoField: IntegerField, models.AutoField: IntegerField,
......
...@@ -10,6 +10,12 @@ a single block in the template. ...@@ -10,6 +10,12 @@ a single block in the template.
background: transparent; background: transparent;
border-top-color: transparent; border-top-color: transparent;
padding-top: 0; 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 { .navbar-inverse .brand a {
...@@ -29,7 +35,7 @@ a single block in the template. ...@@ -29,7 +35,7 @@ a single block in the template.
z-index: 3; z-index: 3;
} }
.navbar .navbar-inner { .navbar {
background: #2C2C2C; background: #2C2C2C;
color: white; color: white;
border: none; border: none;
...@@ -37,7 +43,7 @@ a single block in the template. ...@@ -37,7 +43,7 @@ a single block in the template.
border-radius: 0px; 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; color: white;
} }
...@@ -45,11 +51,11 @@ a single block in the template. ...@@ -45,11 +51,11 @@ a single block in the template.
background: #2C2C2C; 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; color: #A30000;
} }
.navbar .navbar-inner .dropdown-menu li a:hover { .navbar .dropdown-menu li a:hover {
background: #EEEEEE; background: #EEEEEE;
color: #C20000; color: #C20000;
} }
...@@ -61,10 +67,10 @@ html { ...@@ -61,10 +67,10 @@ html {
background: none; background: none;
} }
body, .navbar .navbar-inner .container-fluid { /*body, .navbar .container-fluid {
max-width: 1150px; max-width: 1150px;
margin: 0 auto; margin: 0 auto;
} }*/
body { body {
background: url("../img/grid.png") repeat-x; background: url("../img/grid.png") repeat-x;
......
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
{% block style %} {% block style %}
{% block bootstrap_theme %} {% 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" %}"/> <link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap-tweaks.css" %}"/>
{% endblock %} {% endblock %}
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/prettify.css" %}"/> <link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/prettify.css" %}"/>
...@@ -26,22 +27,21 @@ ...@@ -26,22 +27,21 @@
</head> </head>
{% block body %} {% block body %}
<body class="{% block bodyclass %}{% endblock %} container"> <body class="{% block bodyclass %}{% endblock %}">
<div class="wrapper"> <div class="wrapper">
{% block navbar %} {% block navbar %}
<div class="navbar {% block bootstrap_navbar_variant %}navbar-inverse{% endblock %}"> <div class="navbar navbar-static-top {% block bootstrap_navbar_variant %}navbar-inverse{% endblock %}">
<div class="navbar-inner"> <div class="container">
<div class="container-fluid">
<span> <span>
{% block branding %} {% block branding %}
<a class='brand' rel="nofollow" href='http://www.django-rest-framework.org'> <a class='navbar-brand' rel="nofollow" href='http://www.django-rest-framework.org'>
Django REST framework <span class="version">{{ version }}</span> Django REST framework <span class="version">{{ version }}</span>
</a> </a>
{% endblock %} {% endblock %}
</span> </span>
<ul class="nav pull-right"> <ul class="nav navbar-nav pull-right">
{% block userlinks %} {% block userlinks %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% optional_logout request user %} {% optional_logout request user %}
...@@ -52,18 +52,17 @@ ...@@ -52,18 +52,17 @@
</ul> </ul>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}
<div class="container">
{% block breadcrumbs %} {% block breadcrumbs %}
<ul class="breadcrumb"> <ul class="breadcrumb">
{% for breadcrumb_name, breadcrumb_url in breadcrumblist %} {% for breadcrumb_name, breadcrumb_url in breadcrumblist %}
<li> {% if forloop.last %}
<a href="{{ breadcrumb_url }}" {% if forloop.last %}class="active"{% endif %}> <li class="active">{{ breadcrumb_name }}</li>
{{ breadcrumb_name }} {% else %}
</a> <li><a href="{{ breadcrumb_url }}">{{ breadcrumb_name }}</a></li>
{% if not forloop.last %}<span class="divider">&rsaquo;</span>{% endif %} {% endif %}
</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endblock %} {% endblock %}
...@@ -238,6 +237,7 @@ ...@@ -238,6 +237,7 @@
{% endif %} {% endif %}
</div> </div>
<!-- END Content --> <!-- END Content -->
</div><!-- /.container -->
<footer> <footer>
{% block footer %} {% block footer %}
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">
<div class="checkbox"> <div class="checkbox">
<label> <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 %} {% if field.label %}{{ field.label }}{% endif %}
</label> </label>
</div> </div>
......
...@@ -4,7 +4,7 @@ ...@@ -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> <legend class="control-label col-sm-2 {% if field.style.hide_label %}sr-only{% endif %}" style="border-bottom: 0">{{ field.label }}</legend>
</div> </div>
{% endif %} {% 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) }} {{ renderer.render_field(field_item, layout=layout) }}
{% endfor %} {% endfor %}
</fieldset> </fieldset>
<div class="form-group"> <div class="form-group">
{% include "rest_framework/fields/horizontal/label.html" %} {% include "rest_framework/fields/horizontal/label.html" %}
<div class="col-sm-10"> <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 %} {% if field.help_text %}<p class="help-block">{{ field.help_text }}</p>{% endif %}
</div> </div>
</div> </div>
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<div class="col-sm-10"> <div class="col-sm-10">
<select class="form-control" name="{{ field.field_name }}"> <select class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %} {% 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 %} {% endfor %}
</select> </select>
</div> </div>
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
{% if field.style.inline %} {% if field.style.inline %}
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<label class="checkbox-inline"> <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 }} {{ text }}
</label> </label>
{% endfor %} {% endfor %}
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<div class="checkbox"> <div class="checkbox">
<label> <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 }} {{ text }}
</label> </label>
</div> </div>
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<div class="col-sm-10"> <div class="col-sm-10">
<select multiple class="form-control" name="{{ field.field_name }}"> <select multiple class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %} {% 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 %} {% endfor %}
</select> </select>
</div> </div>
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
{% if field.style.inline %} {% if field.style.inline %}
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<label class="radio-inline"> <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 }} {{ text }}
</label> </label>
{% endfor %} {% endfor %}
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<div class="radio"> <div class="radio">
<label> <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 }} {{ text }}
</label> </label>
</div> </div>
......
<div class="form-group"> <div class="form-group">
{% include "rest_framework/fields/horizontal/label.html" %} {% include "rest_framework/fields/horizontal/label.html" %}
<div class="col-sm-10"> <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 %} {% if field.help_text %}<p class="help-block">{{ field.help_text }}</p>{% endif %}
</div> </div>
</div> </div>
<div class="checkbox"> <div class="checkbox">
<label> <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 %} {% if field.label %}{{ field.label }}{% endif %}
</label> </label>
</div> </div>
{% for field_item in value.field_items.values() %} {% for field_item in field.value.field_items.values() %}
{{ renderer.render_field(field_item, layout=layout) }} {{ renderer.render_field(field_item, layout=layout) }}
{% endfor %} {% endfor %}
<div class="form-group"> <div class="form-group">
{% include "rest_framework/fields/inline/label.html" %} {% 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> </div>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
{% include "rest_framework/fields/inline/label.html" %} {% include "rest_framework/fields/inline/label.html" %}
<select class="form-control" name="{{ field.field_name }}"> <select class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %} {% 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 %} {% endfor %}
</select> </select>
</div> </div>
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<div class="checkbox"> <div class="checkbox">
<label> <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 }} {{ text }}
</label> </label>
</div> </div>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
{% include "rest_framework/fields/inline/label.html" %} {% include "rest_framework/fields/inline/label.html" %}
<select multiple class="form-control" name="{{ field.field_name }}"> <select multiple class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %} {% 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 %} {% endfor %}
</select> </select>
</div> </div>
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<div class="radio"> <div class="radio">
<label> <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 }} {{ text }}
</label> </label>
</div> </div>
......
<div class="form-group"> <div class="form-group">
{% include "rest_framework/fields/inline/label.html" %} {% 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> </div>
<fieldset> <fieldset>
{% if field.label %}<legend {% if field.style.hide_label %}class="sr-only"{% endif %}>{{ field.label }}</legend>{% endif %} {% 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) }} {{ renderer.render_field(field_item, layout=layout) }}
{% endfor %} {% endfor %}
</fieldset> </fieldset>
<div class="form-group"> <div class="form-group">
{% include "rest_framework/fields/vertical/label.html" %} {% 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 %} {% if field.help_text %}<p class="help-block">{{ field.help_text }}</p>{% endif %}
</div> </div>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
{% include "rest_framework/fields/vertical/label.html" %} {% include "rest_framework/fields/vertical/label.html" %}
<select class="form-control" name="{{ field.field_name }}"> <select class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %} {% 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 %} {% endfor %}
</select> </select>
</div> </div>
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<div> <div>
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<label class="checkbox-inline"> <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 }} {{ text }}
</label> </label>
{% endfor %} {% endfor %}
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<div class="checkbox"> <div class="checkbox">
<label> <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 }} {{ text }}
</label> </label>
</div> </div>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
{% include "rest_framework/fields/vertical/label.html" %} {% include "rest_framework/fields/vertical/label.html" %}
<select multiple class="form-control" name="{{ field.field_name }}"> <select multiple class="form-control" name="{{ field.field_name }}">
{% for key, text in field.choices.items %} {% 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 %} {% endfor %}
</select> </select>
</div> </div>
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<div> <div>
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<label class="radio-inline"> <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 }} {{ text }}
</label> </label>
{% endfor %} {% endfor %}
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<div class="radio"> <div class="radio">
<label> <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 }} {{ text }}
</label> </label>
</div> </div>
......
<div class="form-group"> <div class="form-group">
{% include "rest_framework/fields/vertical/label.html" %} {% 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 %} {% if field.help_text %}<p class="help-block">{{ field.help_text }}</p>{% endif %}
</div> </div>
<html> <!-- <html>
<head> <head>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet"> <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
</head> </head>
...@@ -6,13 +6,15 @@ ...@@ -6,13 +6,15 @@
<div class="container"> <div class="container">
<h1>User update</h1> <h1>User update</h1>
<div class="well"> <div class="well"> -->
{% load rest_framework %} {% load rest_framework %}
<form {% if layout == "inline" %}class="form-inline"{% elif layout == "horizontal" %}class="form-horizontal"{% endif %} role="form" action="." method="POST"> <form {% if layout == "inline" %}class="form-inline"{% elif layout == "horizontal" %}class="form-horizontal"{% endif %} role="form" action="." method="POST">
{% csrf_token %} {% csrf_token %}
{% for field, value, errors in form %} {% for field in form %}
{% render_field field value errors layout=layout renderer=renderer %} {% if not field.read_only %}
{% render_field field layout=layout renderer=renderer %}
{% endif %}
{% endfor %} {% endfor %}
<!-- form.non_field_errors --> <!-- form.non_field_errors -->
{% if layout == "horizontal" %} {% if layout == "horizontal" %}
...@@ -25,7 +27,7 @@ ...@@ -25,7 +27,7 @@
<button type="submit" class="btn btn-default">Submit</button> <button type="submit" class="btn btn-default">Submit</button>
{% endif %} {% endif %}
</form> </form>
<!--
</div> </div>
</div></body> </div></body>
</html> </html> -->
...@@ -31,12 +31,9 @@ class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])') ...@@ -31,12 +31,9 @@ class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])')
# And the template tags themselves... # 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 @register.simple_tag
def render_field(field, value, errors, layout=None, renderer=None): def render_field(field, layout=None, renderer=None):
return renderer.render_field(field, value, errors, layout) return renderer.render_field(field, layout)
@register.simple_tag @register.simple_tag
......
...@@ -21,7 +21,14 @@ class ClassLookupDict(object): ...@@ -21,7 +21,14 @@ class ClassLookupDict(object):
self.mapping = mapping self.mapping = mapping
def __getitem__(self, key): 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: if cls in self.mapping:
return self.mapping[cls] return self.mapping[cls]
raise KeyError('Class %s not found in lookup.', cls.__name__) raise KeyError('Class %s not found in lookup.', cls.__name__)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment