widgets.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import copy
  2. from django.forms.widgets import (Widget, Media, TextInput,
  3. SplitDateTimeWidget, DateInput, TimeInput,
  4. MultiWidget, HiddenInput)
  5. from django.utils.safestring import mark_safe
  6. from django.core.validators import EMPTY_VALUES
  7. from django.forms.util import flatatt
  8. class Html5SplitDateTimeWidget(SplitDateTimeWidget):
  9. def __init__(self, attrs=None, date_format=None, time_format=None):
  10. date_input = DateInput(attrs=attrs, format=date_format)
  11. date_input.input_type = 'date'
  12. time_input = TimeInput(attrs=attrs, format=time_format)
  13. time_input.input_type = 'time'
  14. widgets = (date_input, time_input)
  15. MultiWidget.__init__(self, widgets, attrs)
  16. class BaseContainerWidget(Widget):
  17. def __init__(self, data_widget, attrs=None):
  18. if isinstance(data_widget, type):
  19. data_widget = data_widget()
  20. self.data_widget = data_widget
  21. self.data_widget.is_localized = self.is_localized
  22. super(BaseContainerWidget, self).__init__(attrs)
  23. def id_for_label(self, id_):
  24. # See the comment for RadioSelect.id_for_label()
  25. if id_:
  26. id_ += '_0'
  27. return id_
  28. def format_output(self, rendered_widgets):
  29. """
  30. Given a list of rendered widgets (as strings), returns a Unicode string
  31. representing the HTML for the whole lot.
  32. This hook allows you to format the HTML design of the widgets, if
  33. needed.
  34. """
  35. return ''.join(rendered_widgets)
  36. def _get_media(self):
  37. """
  38. Media for a multiwidget is the combination of all media of
  39. the subwidgets.
  40. """
  41. media = Media()
  42. media = media + self.data_widget.media
  43. return media
  44. media = property(_get_media)
  45. def __deepcopy__(self, memo):
  46. obj = super(BaseContainerWidget, self).__deepcopy__(memo)
  47. obj.data_widget = copy.deepcopy(self.data_widget)
  48. return obj
  49. class ListWidget(BaseContainerWidget):
  50. def render(self, name, value, attrs=None):
  51. if value is not None and not isinstance(value, (list, tuple)):
  52. raise TypeError(
  53. "Value supplied for %s must be a list or tuple." % name
  54. )
  55. output = []
  56. value = [] if value is None else value
  57. final_attrs = self.build_attrs(attrs)
  58. id_ = final_attrs.get('id', None)
  59. value.append('')
  60. for i, widget_value in enumerate(value):
  61. if id_:
  62. final_attrs = dict(final_attrs, id='%s_%s' % (id_, i))
  63. output.append(self.data_widget.render(
  64. name + '_%s' % i, widget_value, final_attrs)
  65. )
  66. return mark_safe(self.format_output(output))
  67. def value_from_datadict(self, data, files, name):
  68. widget = self.data_widget
  69. i = 0
  70. ret = []
  71. while (name + '_%s' % i) in data or (name + '_%s' % i) in files:
  72. value = widget.value_from_datadict(data, files, name + '_%s' % i)
  73. # we need a different list if we handle files. Basicly Django sends
  74. # back the initial values if we're not dealing with files. If we
  75. # store files on the list, we need to add empty values to the clean
  76. # data, so the list positions are kept.
  77. if value not in EMPTY_VALUES or (value is None and len(files) > 0):
  78. ret.append(value)
  79. i = i + 1
  80. return ret
  81. class MapWidget(BaseContainerWidget):
  82. def __init__(self, data_widget, attrs=None):
  83. self.key_widget = TextInput()
  84. self.key_widget.is_localized = self.is_localized
  85. super(MapWidget, self).__init__(data_widget, attrs)
  86. def render(self, name, value, attrs=None):
  87. if value is not None and not isinstance(value, dict):
  88. raise TypeError("Value supplied for %s must be a dict." % name)
  89. output = []
  90. final_attrs = self.build_attrs(attrs)
  91. id_ = final_attrs.get('id', None)
  92. fieldset_attr = {}
  93. # in Python 3.X dict.items() returns dynamic *view objects*
  94. value = list(value.items())
  95. value.append(('', ''))
  96. for i, (key, widget_value) in enumerate(value):
  97. if id_:
  98. fieldset_attr = dict(
  99. final_attrs, id='fieldset_%s_%s' % (id_, i)
  100. )
  101. group = []
  102. if not self.is_hidden:
  103. group.append(mark_safe('<fieldset %s>' % flatatt(fieldset_attr)))
  104. if id_:
  105. final_attrs = dict(final_attrs, id='%s_key_%s' % (id_, i))
  106. group.append(self.key_widget.render(
  107. name + '_key_%s' % i, key, final_attrs)
  108. )
  109. if id_:
  110. final_attrs = dict(final_attrs, id='%s_value_%s' % (id_, i))
  111. group.append(self.data_widget.render(
  112. name + '_value_%s' % i, widget_value, final_attrs)
  113. )
  114. if not self.is_hidden:
  115. group.append(mark_safe('</fieldset>'))
  116. output.append(mark_safe(''.join(group)))
  117. return mark_safe(self.format_output(output))
  118. def value_from_datadict(self, data, files, name):
  119. i = 0
  120. ret = {}
  121. while (name + '_key_%s' % i) in data:
  122. key = self.key_widget.value_from_datadict(
  123. data, files, name + '_key_%s' % i
  124. )
  125. value = self.data_widget.value_from_datadict(
  126. data, files, name + '_value_%s' % i
  127. )
  128. if key not in EMPTY_VALUES:
  129. ret.update(((key, value), ))
  130. i = i + 1
  131. return ret
  132. def _get_media(self):
  133. """
  134. Media for a multiwidget is the combination of all media of
  135. the subwidgets.
  136. """
  137. media = super(MapWidget, self)._get_media()
  138. media = media + self.key_widget.media
  139. return media
  140. media = property(_get_media)
  141. def __deepcopy__(self, memo):
  142. obj = super(MapWidget, self).__deepcopy__(memo)
  143. obj.key_widget = copy.deepcopy(self.key_widget)
  144. return obj
  145. class HiddenMapWidget(MapWidget):
  146. is_hidden = True
  147. def __init__(self, attrs=None):
  148. data_widget = HiddenInput()
  149. super(MapWidget, self).__init__(data_widget, attrs)
  150. self.key_widget = HiddenInput()