mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 09:36:49 +03:00
First pass on incorperating the form rendering into the browsable API
This commit is contained in:
parent
ffc6aa3abc
commit
df7b6fcf58
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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 [
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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">›</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 %}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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> -->
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__)
|
||||
|
|
Loading…
Reference in New Issue
Block a user