widgets.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. """
  2. Form Widget classes specific to the Django admin site.
  3. """
  4. from __future__ import absolute_import
  5. from itertools import chain
  6. from django import forms
  7. try:
  8. from django.forms.widgets import ChoiceWidget as RadioChoiceInput
  9. except:
  10. from django.forms.widgets import RadioFieldRenderer, RadioChoiceInput
  11. from django.utils.encoding import force_text
  12. from django.utils.safestring import mark_safe
  13. from django.utils.html import conditional_escape
  14. from django.utils.translation import ugettext as _
  15. from .util import vendor
  16. class AdminDateWidget(forms.DateInput):
  17. @property
  18. def media(self):
  19. return vendor('datepicker.js', 'datepicker.css', 'xadmin.widget.datetime.js')
  20. def __init__(self, attrs=None, format=None):
  21. final_attrs = {'class': 'date-field form-control', 'size': '10'}
  22. if attrs is not None:
  23. final_attrs.update(attrs)
  24. super(AdminDateWidget, self).__init__(attrs=final_attrs, format=format)
  25. def render(self, name, value, attrs=None, renderer=None):
  26. input_html = super(AdminDateWidget, self).render(name, value, attrs, renderer)
  27. return mark_safe('<div class="input-group date bootstrap-datepicker"><span class="input-group-addon"><i class="fa fa-calendar"></i></span>%s'
  28. '<span class="input-group-btn"><button class="btn btn-default" type="button">%s</button></span></div>' % (input_html, _(u'Today')))
  29. class AdminTimeWidget(forms.TimeInput):
  30. @property
  31. def media(self):
  32. return vendor('datepicker.js', 'clockpicker.js', 'clockpicker.css', 'xadmin.widget.datetime.js')
  33. def __init__(self, attrs=None, format=None):
  34. final_attrs = {'class': 'time-field form-control', 'size': '8'}
  35. if attrs is not None:
  36. final_attrs.update(attrs)
  37. super(AdminTimeWidget, self).__init__(attrs=final_attrs, format=format)
  38. def render(self, name, value, attrs=None, renderer=None):
  39. input_html = super(AdminTimeWidget, self).render(name, value, attrs, renderer)
  40. return mark_safe('<div class="input-group time bootstrap-clockpicker"><span class="input-group-addon"><i class="fa fa-clock-o">'
  41. '</i></span>%s<span class="input-group-btn"><button class="btn btn-default" type="button">%s</button></span></div>' % (input_html, _(u'Now')))
  42. class AdminSelectWidget(forms.Select):
  43. @property
  44. def media(self):
  45. return vendor('select.js', 'select.css', 'xadmin.widget.select.js')
  46. class AdminSplitDateTime(forms.SplitDateTimeWidget):
  47. """
  48. A SplitDateTime Widget that has some admin-specific styling.
  49. """
  50. def __init__(self, attrs=None):
  51. widgets = [AdminDateWidget, AdminTimeWidget]
  52. # Note that we're calling MultiWidget, not SplitDateTimeWidget, because
  53. # we want to define widgets.
  54. forms.MultiWidget.__init__(self, widgets, attrs)
  55. def render(self, name, value, attrs=None, renderer=None):
  56. input_html = [ht for ht in super(AdminSplitDateTime, self).render(name, value, attrs, renderer).replace('><input', '>\n<input').split('\n') if ht != '']
  57. # return input_html
  58. return mark_safe('<div class="datetime clearfix"><div class="input-group date bootstrap-datepicker"><span class="input-group-addon"><i class="fa fa-calendar"></i></span>%s'
  59. '<span class="input-group-btn"><button class="btn btn-default" type="button">%s</button></span></div>'
  60. '<div class="input-group time bootstrap-clockpicker"><span class="input-group-addon"><i class="fa fa-clock-o">'
  61. '</i></span>%s<span class="input-group-btn"><button class="btn btn-default" type="button">%s</button></span></div></div>' % (input_html[0], _(u'Today'), input_html[1], _(u'Now')))
  62. def format_output(self, rendered_widgets):
  63. return mark_safe(u'<div class="datetime clearfix">%s%s</div>' %
  64. (rendered_widgets[0], rendered_widgets[1]))
  65. class AdminRadioInput(RadioChoiceInput):
  66. def render(self, name=None, value=None, attrs=None, choices=()):
  67. name = name or self.name
  68. value = value or self.value
  69. attrs = attrs or self.attrs
  70. attrs['class'] = attrs.get('class', '').replace('form-control', '')
  71. if 'id' in self.attrs:
  72. label_for = ' for="%s_%s"' % (self.attrs['id'], self.index)
  73. else:
  74. label_for = ''
  75. choice_label = conditional_escape(force_text(self.choice_label))
  76. if attrs.get('inline', False):
  77. return mark_safe(u'<label%s class="radio-inline">%s %s</label>' % (label_for, self.tag(), choice_label))
  78. else:
  79. return mark_safe(u'<div class="radio"><label%s>%s %s</label></div>' % (label_for, self.tag(), choice_label))
  80. class AdminRadioFieldRenderer(forms.RadioSelect):
  81. def __iter__(self):
  82. for i, choice in enumerate(self.choices):
  83. yield AdminRadioInput(self.name, self.value, self.attrs.copy(), choice, i)
  84. def __getitem__(self, idx):
  85. choice = self.choices[idx] # Let the IndexError propogate
  86. return AdminRadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
  87. def render(self):
  88. return mark_safe(u'\n'.join([force_text(w) for w in self]))
  89. class AdminRadioSelect(forms.RadioSelect):
  90. renderer = AdminRadioFieldRenderer
  91. class AdminCheckboxSelect(forms.CheckboxSelectMultiple):
  92. def render(self, name, value, attrs=None, choices=()):
  93. if value is None:
  94. value = []
  95. has_id = attrs and 'id' in attrs
  96. final_attrs = self.build_attrs(attrs, extra_attrs={'name': name})
  97. output = []
  98. # Normalize to strings
  99. str_values = set([force_text(v) for v in value])
  100. for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
  101. # If an ID attribute was given, add a numeric index as a suffix,
  102. # so that the checkboxes don't all have the same ID attribute.
  103. if has_id:
  104. final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
  105. label_for = u' for="%s"' % final_attrs['id']
  106. else:
  107. label_for = ''
  108. cb = forms.CheckboxInput(
  109. final_attrs, check_test=lambda value: value in str_values)
  110. option_value = force_text(option_value)
  111. rendered_cb = cb.render(name, option_value)
  112. option_label = conditional_escape(force_text(option_label))
  113. if final_attrs.get('inline', False):
  114. output.append(u'<label%s class="checkbox-inline">%s %s</label>' % (label_for, rendered_cb, option_label))
  115. else:
  116. output.append(u'<div class="checkbox"><label%s>%s %s</label></div>' % (label_for, rendered_cb, option_label))
  117. return mark_safe(u'\n'.join(output))
  118. class AdminSelectMultiple(forms.SelectMultiple):
  119. def __init__(self, attrs=None):
  120. final_attrs = {'class': 'select-multi'}
  121. if attrs is not None:
  122. final_attrs.update(attrs)
  123. super(AdminSelectMultiple, self).__init__(attrs=final_attrs)
  124. class AdminFileWidget(forms.ClearableFileInput):
  125. template_with_initial = (u'<p class="file-upload">%s</p>'
  126. % forms.ClearableFileInput.initial_text)
  127. template_with_clear = (u'<span class="clearable-file-input">%s</span>'
  128. % forms.ClearableFileInput.clear_checkbox_label)
  129. class AdminTextareaWidget(forms.Textarea):
  130. def __init__(self, attrs=None):
  131. final_attrs = {'class': 'textarea-field'}
  132. if attrs is not None:
  133. final_attrs.update(attrs)
  134. super(AdminTextareaWidget, self).__init__(attrs=final_attrs)
  135. class AdminTextInputWidget(forms.TextInput):
  136. def __init__(self, attrs=None):
  137. final_attrs = {'class': 'text-field'}
  138. if attrs is not None:
  139. final_attrs.update(attrs)
  140. super(AdminTextInputWidget, self).__init__(attrs=final_attrs)
  141. class AdminURLFieldWidget(forms.TextInput):
  142. def __init__(self, attrs=None):
  143. final_attrs = {'class': 'url-field'}
  144. if attrs is not None:
  145. final_attrs.update(attrs)
  146. super(AdminURLFieldWidget, self).__init__(attrs=final_attrs)
  147. class AdminIntegerFieldWidget(forms.TextInput):
  148. def __init__(self, attrs=None):
  149. final_attrs = {'class': 'int-field'}
  150. if attrs is not None:
  151. final_attrs.update(attrs)
  152. super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs)
  153. class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput):
  154. def __init__(self, attrs=None):
  155. final_attrs = {'class': 'sep-int-field'}
  156. if attrs is not None:
  157. final_attrs.update(attrs)
  158. super(AdminCommaSeparatedIntegerFieldWidget,
  159. self).__init__(attrs=final_attrs)