/usr/lib/python2.7/dist-packages/maasserver/config_forms.py is in python-django-maas 1.5+bzr2252-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | # Copyright 2012 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Config forms utilities."""
from __future__ import (
absolute_import,
print_function,
unicode_literals,
)
str = None
__metaclass__ = type
__all__ = [
'DictCharField',
'DictCharWidget',
'SKIP_CHECK_NAME',
]
from collections import OrderedDict
from django import forms
from django.core import validators
from django.core.exceptions import ValidationError
from django.forms.fields import Field
from django.forms.util import ErrorList
from django.utils.safestring import mark_safe
SKIP_CHECK_NAME = 'skip_check'
class DictCharField(forms.MultiValueField):
"""A field to edit a dictionary of strings. Each entry in the
dictionary corresponds to a sub-field.
The field is constructed with a list of tuples containing the name of the
sub-fields and the sub-field themselves. An optional parameter
'skip_check' allows the storing of an arbitrary dictionary in the field,
bypassing any validation made by the sub-fields.
For instance, if we create a form with a single DictCharField::
>>> class ExampleForm(forms.Form):
... example = DictCharField([
... ('field1', forms.CharField(label="Field 1")),
... ('field2', forms.CharField(label="Field 2")),
... ])
>>> from django.http import QueryDict
>>> data = QueryDict('example_field1=subvalue1&example_field2=subvalue2')
>>> form = ExampleForm(data)
>>> # The 'cleaned_data' of the 'example' field is populated with the
>>> # values of the subfields.
>>> form.cleaned_data['example']
{'field1': 'subvalue1', 'field2': 'subvalue2'}
"""
def __init__(self, field_items, skip_check=False, *args,
**kwargs):
self.field_dict = OrderedDict(field_items)
self.skip_check = skip_check
# Make sure no subfield is named 'SKIP_CHECK_NAME'. If
# skip_check is True this field will clash with the addtional
# subfield added by the DictCharField constructor. We perform
# this check even if skip_check=False because having a field named
# 'skip_check' that isn't used to actually skip the checks would be
# very confusing.
if SKIP_CHECK_NAME in self.field_dict.keys():
raise RuntimeError(
"'%s' is a reserved name "
"(it can't be used to name a subfield)." % SKIP_CHECK_NAME)
# if skip_check: add a BooleanField to the list of fields, this will
# be used to skip the validation of the fields and accept arbitrary
# data.
if skip_check:
self.field_dict[SKIP_CHECK_NAME] = forms.BooleanField(
required=False)
self.names = [name for name in self.field_dict.keys()]
# Create the DictCharWidget with init values from the list of fields.
self.fields = self.field_dict.values()
self.widget = DictCharWidget(
[field.widget for field in self.fields],
self.names,
[field.initial for field in self.fields],
[field.label for field in self.fields],
skip_check=skip_check,
)
# Upcall to Field and not MultiValueField to avoid setting all the
# subfields' 'required' attributes to False.
Field.__init__(self, *args, **kwargs)
def compress(self, data):
"""Returns a single value for the given list of values."""
if data:
if isinstance(data, dict):
# If the data is a dict, this means that we're in the
# situation where skip_check was true and we simply
# return the dict.
return data
else:
# Here data is the list of the values of the subfields,
# return a dict with all the right keys:
# For instance, for a DictCharField created with two
# subfields 'field1' and 'field2', data will be
# ['value1', 'value2'] and we will return:
# {'field1': 'value1', 'field2': 'value2'}
return dict(zip(self.names, data))
return None
def get_names(self):
if self.skip_check:
return self.names[:-1]
else:
return self.names
def clean(self, value):
"""Validates every value in the given list. A value is validated
against the corresponding Field in self.fields.
This is an adapted version of Django's MultiValueField_ clean method.
The differences are:
- the method is split into clean_global_empty and
clean_sub_fields;
- the field and value corresponding to the SKIP_CHECK_NAME boolean
field are removed;
- each individual field 'required' attribute is used instead of the
DictCharField's 'required' attribute. This allows a more
fine-grained control of what's required and what's not required.
.. _MultiValueField: http://code.djangoproject.com/
svn/django/tags/releases/1.3.1/django/forms/fields.py
"""
skip_check = (
self.skip_check and
self.widget.widgets[-1].value_from_datadict(
value, files=None, name=SKIP_CHECK_NAME))
# Remove the 'skip_check' value from the list of values.
try:
value.pop(SKIP_CHECK_NAME)
except KeyError:
pass
if skip_check:
# If the skip_check option is on and the value of the boolean
# field is true: don't perform any validation and simply return
# the dictionary.
return value
else:
self.clean_unknown_params(value)
values = [value.get(name) for name in self.get_names()]
result = self.clean_global_empty(values)
if result is None:
return None
else:
return self.clean_sub_fields(values)
def clean_unknown_params(self, value):
unknown_params = set(value.keys()).difference(self.get_names())
if len(unknown_params) != 0:
raise ValidationError(
"Unknown parameter(s): %s." % ', '.join(unknown_params))
def clean_global_empty(self, value):
"""Make sure the value is not empty and is thus suitable to be
feed to the sub fields' validators."""
if not value or isinstance(value, (list, tuple)):
# value is considered empty if it is in
# validators.EMPTY_VALUES, or if each of the subvalues is
# None.
is_empty = (
value in validators.EMPTY_VALUES or
len(filter(lambda x: x is not None, value)) == 0)
if is_empty:
if self.required:
raise ValidationError(self.error_messages['required'])
else:
return None
else:
return True
else:
raise ValidationError(self.error_messages['invalid'])
def clean_sub_fields(self, value):
"""'value' being the list of the values of the subfields, validate
each subfield."""
clean_data = []
errors = ErrorList()
# Remove the field corresponding to the SKIP_CHECK_NAME boolean field
# if required.
fields = self.fields if not self.skip_check else self.fields[:-1]
for index, field in enumerate(fields):
try:
field_value = value[index]
except IndexError:
field_value = None
# Check the field's 'required' field instead of the global
# 'required' field to allow subfields to be required or not.
if field.required and field_value in validators.EMPTY_VALUES:
errors.append(
'%s: %s' % (field.label, self.error_messages['required']))
continue
try:
clean_data.append(field.clean(field_value))
except ValidationError, e:
# Collect all validation errors in a single list, which we'll
# raise at the end of clean(), rather than raising a single
# exception for the first error we encounter.
errors.extend(
'%s: %s' % (field.label, message)
for message in e.messages)
if errors:
raise ValidationError(errors)
out = self.compress(clean_data)
self.validate(out)
return out
def get_all_prefixed_values(data, name):
"""From a dictionary, extract a sub-dictionary of all the keys/values for
which the key starts with a particular prefix. In the resulting
dictionary, strip the prefix from the keys::
>>> get_all_prefixed_values(
... {'prefix_test': 'a', 'key': 'b'}, 'prefix_')
{'test': 'a'}
"""
result = {}
for key, value in data.items():
if key.startswith(name):
new_key = key[len(name):]
result[new_key] = value
return result
class DictCharWidget(forms.widgets.MultiWidget):
"""A widget to display the content of a dictionary. Each key will
correspond to a subwidget. Although there is no harm in using this class
directly, note that this is mostly destined to be used internally
by DictCharField.
The customization compared to Django's MultiWidget_ are:
- DictCharWidget displays all the subwidgets inside a fieldset tag;
- DictCharWidget displays a label for each subwidget;
- DictCharWidget names each subwidget 'main_widget_sub_widget_name'
instead of 'main_widget_0';
- DictCharWidget has the (optional) ability to skip all the validation
and instead fetch all the values prefixed by 'main_widget_' in the
input data.
To achieve that, we customize:
- 'render' which returns the HTML code to display this widget;
- 'id_for_label' which return the HTML ID attribute for this widget
for use by a label. This widget is composed of multiple widgets so
the id of the first widget is used;
- 'value_from_datadict' which fetches the value of the data to be
processed by this form to give a 'data' dictionary. We need to
customize that because we've changed the way MultiWidget names
sub-widgets;
- 'decompress' which takes a single "compressed" value and returns a list
of values to be used by the widgets.
.. _MultiWidget: http://code.djangoproject.com/
svn/django/tags/releases/1.3.1/django/forms/widgets.py
Arguments:
widgets -- list of widgets for sub-fields.
names -- list of names for sub-fields.
initials -- list of initial values for sub-fields.
labels -- list of labels for sub-fields
Keyword arguments:
skip_check -- boolean indicating validation will be skipped.
attrs -- see Widget.attrs
"""
def __init__(self, widgets, names,
initials, labels, skip_check=False, attrs=None):
self.names = names
self.initials = initials
self.labels = labels
self.skip_check = skip_check
super(DictCharWidget, self).__init__(widgets, attrs)
def render(self, name, value, attrs=None):
# value is a list of values, each corresponding to a widget
# in self.widgets.
# Do not display the 'skip_check' boolean widget.
if self.skip_check:
widgets = self.widgets[:-1]
else:
widgets = self.widgets
if not isinstance(value, list):
value = self.decompress(value)
if len(widgets) == 0:
return mark_safe(self.format_output(''))
output = ['<fieldset>']
final_attrs = self.build_attrs(attrs)
id_ = final_attrs.get('id', None)
for index, widget in enumerate(widgets):
try:
widget_value = value[index]
except IndexError:
try:
widget_value = self.initials[index]
except IndexError:
widget_value = None
if id_:
final_attrs = dict(
final_attrs, id='%s_%s' % (id_, self.names[index]))
# Add label to each sub-field.
if id_:
label_for = ' for="%s"' % final_attrs['id']
else:
label_for = ''
output.append(
'<label%s>%s</label>' % (
label_for, self.labels[index]))
output.append(
widget.render(
'%s_%s' % (name, self.names[index]), widget_value,
final_attrs))
output.append('</fieldset>')
return mark_safe(self.format_output(output))
def id_for_label(self, id_):
"""Returns the HTML ID attribute of this Widget. Since this is a
widget with multiple HTML elements, this method returns an ID
corresponding to the first ID in the widget's tags."""
# See the comment for RadioSelect.id_for_label()
if id_:
id_ += '_%s' % self.names[0]
return id_
def value_from_datadict(self, data, files, name):
"""Extract the values for this widget from a data dict (QueryDict).
:param data: The data dict (usually request.data or request.GET where
request is a django.http.HttpRequest).
:type data: dict
:param files: The files dict (usually request.FILES where request is a
django.http.HttpRequest).
:type files: dict
:param name: The name of the widget.
:type name: unicode
:return: The extracted values as a dictionary.
:rtype: dict or list
"""
return get_all_prefixed_values(data, name + '_')
def decompress(self, value):
"""Returns a list of decompressed values for the given compressed
value. The given value can be assumed to be valid, but not
necessarily non-empty."""
if value not in validators.EMPTY_VALUES:
return [value.get(name, None) for name in self.names]
else:
return [None] * len(self.names)
|