123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864 |
- """
- HTML Widget classes
- """
- from __future__ import absolute_import
- import copy
- import datetime
- from itertools import chain
- from urlparse import urljoin
- from django.conf import settings
- from django.forms.util import flatatt, to_current_timezone
- from django.utils.datastructures import MultiValueDict, MergeDict
- from django.utils.html import escape, conditional_escape
- from django.utils.translation import ugettext, ugettext_lazy
- from django.utils.encoding import StrAndUnicode, force_unicode
- from django.utils.safestring import mark_safe
- from django.utils import datetime_safe, formats
- __all__ = (
- 'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'PasswordInput',
- 'HiddenInput', 'MultipleHiddenInput', 'ClearableFileInput',
- 'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput',
- 'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
- 'CheckboxSelectMultiple', 'MultiWidget',
- 'SplitDateTimeWidget',
- )
- MEDIA_TYPES = ('css','js')
- class Media(StrAndUnicode):
- def __init__(self, media=None, **kwargs):
- if media:
- media_attrs = media.__dict__
- else:
- media_attrs = kwargs
- self._css = {}
- self._js = []
- for name in MEDIA_TYPES:
- getattr(self, 'add_' + name)(media_attrs.get(name, None))
-
-
-
- def __unicode__(self):
- return self.render()
- def render(self):
- return mark_safe(u'\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES])))
- def render_js(self):
- return [u'<script type="text/javascript" src="%s"></script>' % self.absolute_path(path) for path in self._js]
- def render_css(self):
-
-
- media = self._css.keys()
- media.sort()
- return chain(*[
- [u'<link href="%s" type="text/css" media="%s" rel="stylesheet" />' % (self.absolute_path(path), medium)
- for path in self._css[medium]]
- for medium in media])
- def absolute_path(self, path, prefix=None):
- if path.startswith(u'http://') or path.startswith(u'https://') or path.startswith(u'/'):
- return path
- if prefix is None:
- if settings.STATIC_URL is None:
-
- prefix = settings.MEDIA_URL
- else:
- prefix = settings.STATIC_URL
- return urljoin(prefix, path)
- def __getitem__(self, name):
- "Returns a Media object that only contains media of the given type"
- if name in MEDIA_TYPES:
- return Media(**{str(name): getattr(self, '_' + name)})
- raise KeyError('Unknown media type "%s"' % name)
- def add_js(self, data):
- if data:
- for path in data:
- if path not in self._js:
- self._js.append(path)
- def add_css(self, data):
- if data:
- for medium, paths in data.items():
- for path in paths:
- if not self._css.get(medium) or path not in self._css[medium]:
- self._css.setdefault(medium, []).append(path)
- def __add__(self, other):
- combined = Media()
- for name in MEDIA_TYPES:
- getattr(combined, 'add_' + name)(getattr(self, '_' + name, None))
- getattr(combined, 'add_' + name)(getattr(other, '_' + name, None))
- return combined
- def media_property(cls):
- def _media(self):
-
- if hasattr(super(cls, self), 'media'):
- base = super(cls, self).media
- else:
- base = Media()
-
- definition = getattr(cls, 'Media', None)
- if definition:
- extend = getattr(definition, 'extend', True)
- if extend:
- if extend == True:
- m = base
- else:
- m = Media()
- for medium in extend:
- m = m + base[medium]
- return m + Media(definition)
- else:
- return Media(definition)
- else:
- return base
- return property(_media)
- class MediaDefiningClass(type):
- "Metaclass for classes that can have media definitions"
- def __new__(cls, name, bases, attrs):
- new_class = super(MediaDefiningClass, cls).__new__(cls, name, bases,
- attrs)
- if 'media' not in attrs:
- new_class.media = media_property(new_class)
- return new_class
- class Widget(object):
- __metaclass__ = MediaDefiningClass
- is_hidden = False
- needs_multipart_form = False
- is_localized = False
- is_required = False
- def __init__(self, attrs=None):
- if attrs is not None:
- self.attrs = attrs.copy()
- else:
- self.attrs = {}
- def __deepcopy__(self, memo):
- obj = copy.copy(self)
- obj.attrs = self.attrs.copy()
- memo[id(self)] = obj
- return obj
- def render(self, name, value, attrs=None):
- """
- Returns this Widget rendered as HTML, as a Unicode string.
- The 'value' given is not guaranteed to be valid input, so subclass
- implementations should program defensively.
- """
- raise NotImplementedError
- def build_attrs(self, extra_attrs=None, **kwargs):
- "Helper function for building an attribute dictionary."
- attrs = dict(self.attrs, **kwargs)
- if extra_attrs:
- attrs.update(extra_attrs)
- return attrs
- def value_from_datadict(self, data, files, name):
- """
- Given a dictionary of data and this widget's name, returns the value
- of this widget. Returns None if it's not provided.
- """
- return data.get(name, None)
- def _has_changed(self, initial, data):
- """
- Return True if data differs from initial.
- """
-
-
-
- if data is None:
- data_value = u''
- else:
- data_value = data
- if initial is None:
- initial_value = u''
- else:
- initial_value = initial
- if force_unicode(initial_value) != force_unicode(data_value):
- return True
- return False
- def id_for_label(self, id_):
- """
- Returns the HTML ID attribute of this Widget for use by a <label>,
- given the ID of the field. Returns None if no ID is available.
- This hook is necessary because some widgets have multiple HTML
- elements and, thus, multiple IDs. In that case, this method should
- return an ID value that corresponds to the first ID in the widget's
- tags.
- """
- return id_
- id_for_label = classmethod(id_for_label)
- class Input(Widget):
- """
- Base class for all <input> widgets (except type='checkbox' and
- type='radio', which are special).
- """
- input_type = None
- def _format_value(self, value):
- if self.is_localized:
- return formats.localize_input(value)
- return value
- def render(self, name, value, attrs=None):
- if value is None:
- value = ''
- final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
- if value != '':
-
- final_attrs['value'] = force_unicode(self._format_value(value))
- return mark_safe(u'<input%s />' % flatatt(final_attrs))
- class TextInput(Input):
- input_type = 'text'
- class PasswordInput(Input):
- input_type = 'password'
- def __init__(self, attrs=None, render_value=False):
- super(PasswordInput, self).__init__(attrs)
- self.render_value = render_value
- def render(self, name, value, attrs=None):
- if not self.render_value: value=None
- return super(PasswordInput, self).render(name, value, attrs)
- class HiddenInput(Input):
- input_type = 'hidden'
- is_hidden = True
- class MultipleHiddenInput(HiddenInput):
- """
- A widget that handles <input type="hidden"> for fields that have a list
- of values.
- """
- def __init__(self, attrs=None, choices=()):
- super(MultipleHiddenInput, self).__init__(attrs)
-
- self.choices = choices
- def render(self, name, value, attrs=None, choices=()):
- if value is None: value = []
- final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
- id_ = final_attrs.get('id', None)
- inputs = []
- for i, v in enumerate(value):
- input_attrs = dict(value=force_unicode(v), **final_attrs)
- if id_:
-
-
- input_attrs['id'] = '%s_%s' % (id_, i)
- inputs.append(u'<input%s />' % flatatt(input_attrs))
- return mark_safe(u'\n'.join(inputs))
- def value_from_datadict(self, data, files, name):
- if isinstance(data, (MultiValueDict, MergeDict)):
- return data.getlist(name)
- return data.get(name, None)
- class FileInput(Input):
- input_type = 'file'
- needs_multipart_form = True
- def render(self, name, value, attrs=None):
- return super(FileInput, self).render(name, None, attrs=attrs)
- def value_from_datadict(self, data, files, name):
- "File widgets take data from FILES, not POST"
- return files.get(name, None)
- def _has_changed(self, initial, data):
- if data is None:
- return False
- return True
- FILE_INPUT_CONTRADICTION = object()
- class ClearableFileInput(FileInput):
- initial_text = ugettext_lazy('Currently')
- input_text = ugettext_lazy('Change')
- clear_checkbox_label = ugettext_lazy('Clear')
- template_with_initial = u'%(initial_text)s: %(initial)s %(clear_template)s<br />%(input_text)s: %(input)s'
- template_with_clear = u'%(clear)s <label for="%(clear_checkbox_id)s">%(clear_checkbox_label)s</label>'
- def clear_checkbox_name(self, name):
- """
- Given the name of the file input, return the name of the clear checkbox
- input.
- """
- return name + '-clear'
- def clear_checkbox_id(self, name):
- """
- Given the name of the clear checkbox input, return the HTML id for it.
- """
- return name + '_id'
- def render(self, name, value, attrs=None):
- substitutions = {
- 'initial_text': self.initial_text,
- 'input_text': self.input_text,
- 'clear_template': '',
- 'clear_checkbox_label': self.clear_checkbox_label,
- }
- template = u'%(input)s'
- substitutions['input'] = super(ClearableFileInput, self).render(name, value, attrs)
- if value and hasattr(value, "url"):
- template = self.template_with_initial
- substitutions['initial'] = (u'<a href="%s">%s</a>'
- % (escape(value.url),
- escape(force_unicode(value))))
- if not self.is_required:
- checkbox_name = self.clear_checkbox_name(name)
- checkbox_id = self.clear_checkbox_id(checkbox_name)
- substitutions['clear_checkbox_name'] = conditional_escape(checkbox_name)
- substitutions['clear_checkbox_id'] = conditional_escape(checkbox_id)
- substitutions['clear'] = CheckboxInput().render(checkbox_name, False, attrs={'id': checkbox_id})
- substitutions['clear_template'] = self.template_with_clear % substitutions
- return mark_safe(template % substitutions)
- def value_from_datadict(self, data, files, name):
- upload = super(ClearableFileInput, self).value_from_datadict(data, files, name)
- if not self.is_required and CheckboxInput().value_from_datadict(
- data, files, self.clear_checkbox_name(name)):
- if upload:
-
-
-
- return FILE_INPUT_CONTRADICTION
-
- return False
- return upload
- class Textarea(Widget):
- def __init__(self, attrs=None):
-
- default_attrs = {'cols': '40', 'rows': '10'}
- if attrs:
- default_attrs.update(attrs)
- super(Textarea, self).__init__(default_attrs)
- def render(self, name, value, attrs=None):
- if value is None: value = ''
- final_attrs = self.build_attrs(attrs, name=name)
- return mark_safe(u'<textarea%s>%s</textarea>' % (flatatt(final_attrs),
- conditional_escape(force_unicode(value))))
- class DateInput(Input):
- input_type = 'text'
- def __init__(self, attrs=None, format=None):
- super(DateInput, self).__init__(attrs)
- if format:
- self.format = format
- self.manual_format = True
- else:
- self.format = formats.get_format('DATE_INPUT_FORMATS')[0]
- self.manual_format = False
- def _format_value(self, value):
- if self.is_localized and not self.manual_format:
- return formats.localize_input(value)
- elif hasattr(value, 'strftime'):
- value = datetime_safe.new_date(value)
- return value.strftime(self.format)
- return value
- def _has_changed(self, initial, data):
-
-
-
- try:
- input_format = formats.get_format('DATE_INPUT_FORMATS')[0]
- initial = datetime.datetime.strptime(initial, input_format).date()
- except (TypeError, ValueError):
- pass
- return super(DateInput, self)._has_changed(self._format_value(initial), data)
- class DateTimeInput(Input):
- input_type = 'text'
- def __init__(self, attrs=None, format=None):
- super(DateTimeInput, self).__init__(attrs)
- if format:
- self.format = format
- self.manual_format = True
- else:
- self.format = formats.get_format('DATETIME_INPUT_FORMATS')[0]
- self.manual_format = False
- def _format_value(self, value):
- if self.is_localized and not self.manual_format:
- return formats.localize_input(value)
- elif hasattr(value, 'strftime'):
- value = datetime_safe.new_datetime(value)
- return value.strftime(self.format)
- return value
- def _has_changed(self, initial, data):
-
-
-
- try:
- input_format = formats.get_format('DATETIME_INPUT_FORMATS')[0]
- initial = datetime.datetime.strptime(initial, input_format)
- except (TypeError, ValueError):
- pass
- return super(DateTimeInput, self)._has_changed(self._format_value(initial), data)
- class TimeInput(Input):
- input_type = 'text'
- def __init__(self, attrs=None, format=None):
- super(TimeInput, self).__init__(attrs)
- if format:
- self.format = format
- self.manual_format = True
- else:
- self.format = formats.get_format('TIME_INPUT_FORMATS')[0]
- self.manual_format = False
- def _format_value(self, value):
- if self.is_localized and not self.manual_format:
- return formats.localize_input(value)
- elif hasattr(value, 'strftime'):
- return value.strftime(self.format)
- return value
- def _has_changed(self, initial, data):
-
-
-
- try:
- input_format = formats.get_format('TIME_INPUT_FORMATS')[0]
- initial = datetime.datetime.strptime(initial, input_format).time()
- except (TypeError, ValueError):
- pass
- return super(TimeInput, self)._has_changed(self._format_value(initial), data)
- class CheckboxInput(Widget):
- def __init__(self, attrs=None, check_test=bool):
- super(CheckboxInput, self).__init__(attrs)
-
-
- self.check_test = check_test
- def render(self, name, value, attrs=None):
- final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
- try:
- result = self.check_test(value)
- except:
- result = False
- if result:
- final_attrs['checked'] = 'checked'
- if value not in ('', True, False, None):
-
- final_attrs['value'] = force_unicode(value)
- return mark_safe(u'<input%s />' % flatatt(final_attrs))
- def value_from_datadict(self, data, files, name):
- if name not in data:
-
-
- return False
- value = data.get(name)
-
- values = {'true': True, 'false': False}
- if isinstance(value, basestring):
- value = values.get(value.lower(), value)
- return value
- def _has_changed(self, initial, data):
-
-
- return bool(initial) != bool(data)
- class Select(Widget):
- allow_multiple_selected = False
- def __init__(self, attrs=None, choices=()):
- super(Select, self).__init__(attrs)
-
-
-
- self.choices = list(choices)
- def render(self, name, value, attrs=None, choices=()):
- if value is None: value = ''
- final_attrs = self.build_attrs(attrs, name=name)
- output = [u'<select%s>' % flatatt(final_attrs)]
- options = self.render_options(choices, [value])
- if options:
- output.append(options)
- output.append(u'</select>')
- return mark_safe(u'\n'.join(output))
- def render_option(self, selected_choices, option_value, option_label):
- option_value = force_unicode(option_value)
- if option_value in selected_choices:
- selected_html = u' selected="selected"'
- if not self.allow_multiple_selected:
-
- selected_choices.remove(option_value)
- else:
- selected_html = ''
- return u'<option value="%s"%s>%s</option>' % (
- escape(option_value), selected_html,
- conditional_escape(force_unicode(option_label)))
- def render_options(self, choices, selected_choices):
-
- selected_choices = set(force_unicode(v) for v in selected_choices)
- output = []
- for option_value, option_label in chain(self.choices, choices):
- if isinstance(option_label, (list, tuple)):
- output.append(u'<optgroup label="%s">' % escape(force_unicode(option_value)))
- for option in option_label:
- output.append(self.render_option(selected_choices, *option))
- output.append(u'</optgroup>')
- else:
- output.append(self.render_option(selected_choices, option_value, option_label))
- return u'\n'.join(output)
- class NullBooleanSelect(Select):
- """
- A Select Widget intended to be used with NullBooleanField.
- """
- def __init__(self, attrs=None):
- choices = ((u'1', ugettext_lazy('Unknown')),
- (u'2', ugettext_lazy('Yes')),
- (u'3', ugettext_lazy('No')))
- super(NullBooleanSelect, self).__init__(attrs, choices)
- def render(self, name, value, attrs=None, choices=()):
- try:
- value = {True: u'2', False: u'3', u'2': u'2', u'3': u'3'}[value]
- except KeyError:
- value = u'1'
- return super(NullBooleanSelect, self).render(name, value, attrs, choices)
- def value_from_datadict(self, data, files, name):
- value = data.get(name, None)
- return {u'2': True,
- True: True,
- 'True': True,
- u'3': False,
- 'False': False,
- False: False}.get(value, None)
- def _has_changed(self, initial, data):
-
-
- if initial is not None:
- initial = bool(initial)
- if data is not None:
- data = bool(data)
- return initial != data
- class SelectMultiple(Select):
- allow_multiple_selected = True
- def render(self, name, value, attrs=None, choices=()):
- if value is None: value = []
- final_attrs = self.build_attrs(attrs, name=name)
- output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)]
- options = self.render_options(choices, value)
- if options:
- output.append(options)
- output.append('</select>')
- return mark_safe(u'\n'.join(output))
- def value_from_datadict(self, data, files, name):
- if isinstance(data, (MultiValueDict, MergeDict)):
- return data.getlist(name)
- return data.get(name, None)
- def _has_changed(self, initial, data):
- if initial is None:
- initial = []
- if data is None:
- data = []
- if len(initial) != len(data):
- return True
- initial_set = set([force_unicode(value) for value in initial])
- data_set = set([force_unicode(value) for value in data])
- return data_set != initial_set
- class RadioInput(StrAndUnicode):
- """
- An object used by RadioFieldRenderer that represents a single
- <input type='radio'>.
- """
- def __init__(self, name, value, attrs, choice, index):
- self.name, self.value = name, value
- self.attrs = attrs
- self.choice_value = force_unicode(choice[0])
- self.choice_label = force_unicode(choice[1])
- self.index = index
- def __unicode__(self):
- if 'id' in self.attrs:
- label_for = ' for="%s_%s"' % (self.attrs['id'], self.index)
- else:
- label_for = ''
- choice_label = conditional_escape(force_unicode(self.choice_label))
- return mark_safe(u'<label%s>%s %s</label>' % (label_for, self.tag(), choice_label))
- def is_checked(self):
- return self.value == self.choice_value
- def tag(self):
- if 'id' in self.attrs:
- self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index)
- final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
- if self.is_checked():
- final_attrs['checked'] = 'checked'
- return mark_safe(u'<input%s />' % flatatt(final_attrs))
- class RadioFieldRenderer(StrAndUnicode):
- """
- An object used by RadioSelect to enable customization of radio widgets.
- """
- def __init__(self, name, value, attrs, choices):
- self.name, self.value, self.attrs = name, value, attrs
- self.choices = choices
- def __iter__(self):
- for i, choice in enumerate(self.choices):
- yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
- def __getitem__(self, idx):
- choice = self.choices[idx]
- return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
- def __unicode__(self):
- return self.render()
- def render(self):
- """Outputs a <ul> for this set of radio fields."""
- return mark_safe(u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>'
- % force_unicode(w) for w in self]))
- class RadioSelect(Select):
- renderer = RadioFieldRenderer
- def __init__(self, *args, **kwargs):
-
- renderer = kwargs.pop('renderer', None)
- if renderer:
- self.renderer = renderer
- super(RadioSelect, self).__init__(*args, **kwargs)
- def get_renderer(self, name, value, attrs=None, choices=()):
- """Returns an instance of the renderer."""
- if value is None: value = ''
- str_value = force_unicode(value)
- final_attrs = self.build_attrs(attrs)
- choices = list(chain(self.choices, choices))
- return self.renderer(name, str_value, final_attrs, choices)
- def render(self, name, value, attrs=None, choices=()):
- return self.get_renderer(name, value, attrs, choices).render()
- def id_for_label(self, id_):
-
-
-
-
- if id_:
- id_ += '_0'
- return id_
- id_for_label = classmethod(id_for_label)
- class CheckboxSelectMultiple(SelectMultiple):
- def render(self, name, value, attrs=None, choices=()):
- if value is None: value = []
- has_id = attrs and 'id' in attrs
- final_attrs = self.build_attrs(attrs, name=name)
- output = [u'<ul>']
-
- str_values = set([force_unicode(v) for v in value])
- for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
-
-
- if has_id:
- final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
- label_for = u' for="%s"' % final_attrs['id']
- else:
- label_for = ''
- cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
- option_value = force_unicode(option_value)
- rendered_cb = cb.render(name, option_value)
- option_label = conditional_escape(force_unicode(option_label))
- output.append(u'<li><label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label))
- output.append(u'</ul>')
- return mark_safe(u'\n'.join(output))
- def id_for_label(self, id_):
-
- if id_:
- id_ += '_0'
- return id_
- id_for_label = classmethod(id_for_label)
- class MultiWidget(Widget):
- """
- A widget that is composed of multiple widgets.
- Its render() method is different than other widgets', because it has to
- figure out how to split a single value for display in multiple widgets.
- The ``value`` argument can be one of two things:
- * A list.
- * A normal value (e.g., a string) that has been "compressed" from
- a list of values.
- In the second case -- i.e., if the value is NOT a list -- render() will
- first "decompress" the value into a list before rendering it. It does so by
- calling the decompress() method, which MultiWidget subclasses must
- implement. This method takes a single "compressed" value and returns a
- list.
- When render() does its HTML rendering, each value in the list is rendered
- with the corresponding widget -- the first value is rendered in the first
- widget, the second value is rendered in the second widget, etc.
- Subclasses may implement format_output(), which takes the list of rendered
- widgets and returns a string of HTML that formats them any way you'd like.
- You'll probably want to use this class with MultiValueField.
- """
- def __init__(self, widgets, attrs=None):
- self.widgets = [isinstance(w, type) and w() or w for w in widgets]
- super(MultiWidget, self).__init__(attrs)
- def render(self, name, value, attrs=None):
- if self.is_localized:
- for widget in self.widgets:
- widget.is_localized = self.is_localized
-
-
- if not isinstance(value, list):
- value = self.decompress(value)
- output = []
- final_attrs = self.build_attrs(attrs)
- id_ = final_attrs.get('id', None)
- for i, widget in enumerate(self.widgets):
- try:
- widget_value = value[i]
- except IndexError:
- widget_value = None
- if id_:
- final_attrs = dict(final_attrs, id='%s_%s' % (id_, i))
- output.append(widget.render(name + '_%s' % i, widget_value, final_attrs))
- return mark_safe(self.format_output(output))
- def id_for_label(self, id_):
-
- if id_:
- id_ += '_0'
- return id_
- id_for_label = classmethod(id_for_label)
- def value_from_datadict(self, data, files, name):
- return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
- def _has_changed(self, initial, data):
- if initial is None:
- initial = [u'' for x in range(0, len(data))]
- else:
- if not isinstance(initial, list):
- initial = self.decompress(initial)
- for widget, initial, data in zip(self.widgets, initial, data):
- if widget._has_changed(initial, data):
- return True
- return False
- def format_output(self, rendered_widgets):
- """
- Given a list of rendered widgets (as strings), returns a Unicode string
- representing the HTML for the whole lot.
- This hook allows you to format the HTML design of the widgets, if
- needed.
- """
- return u''.join(rendered_widgets)
- 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.
- """
- raise NotImplementedError('Subclasses must implement this method.')
- def _get_media(self):
- "Media for a multiwidget is the combination of all media of the subwidgets"
- media = Media()
- for w in self.widgets:
- media = media + w.media
- return media
- media = property(_get_media)
- def __deepcopy__(self, memo):
- obj = super(MultiWidget, self).__deepcopy__(memo)
- obj.widgets = copy.deepcopy(self.widgets)
- return obj
- class SplitDateTimeWidget(MultiWidget):
- """
- A Widget that splits datetime input into two <input type="text"> boxes.
- """
- def __init__(self, attrs=None, date_format=None, time_format=None):
- widgets = (DateInput(attrs=attrs, format=date_format),
- TimeInput(attrs=attrs, format=time_format))
- super(SplitDateTimeWidget, self).__init__(widgets, attrs)
- def decompress(self, value):
- if value:
- value = to_current_timezone(value)
- return [value.date(), value.time().replace(microsecond=0)]
- return [None, None]
- class SplitHiddenDateTimeWidget(SplitDateTimeWidget):
- """
- A Widget that splits datetime input into two <input type="hidden"> inputs.
- """
- is_hidden = True
- def __init__(self, attrs=None, date_format=None, time_format=None):
- super(SplitHiddenDateTimeWidget, self).__init__(attrs, date_format, time_format)
- for widget in self.widgets:
- widget.input_type = 'hidden'
- widget.is_hidden = True
|