您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

widgets.py 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. from django.core.files.uploadedfile import InMemoryUploadedFile
  2. import re
  3. import six
  4. from django import forms
  5. from django.forms.util import flatatt
  6. from django.forms.widgets import FileInput
  7. from django.template import Context
  8. from django.template.loader import render_to_string
  9. from django.utils.encoding import force_text
  10. from django.utils.safestring import mark_safe
  11. from django.utils.html import format_html
  12. class ImageInput(FileInput):
  13. """
  14. Widget providing a input element for file uploads based on the
  15. Django ``FileInput`` element. It hides the actual browser-specific
  16. input element and shows the available image for images that have
  17. been previously uploaded. Selecting the image will open the file
  18. dialog and allow for selecting a new or replacing image file.
  19. """
  20. template_name = 'partials/image_input_widget.html'
  21. attrs = {'accept': 'image/*'}
  22. def render(self, name, value, attrs=None):
  23. """
  24. Render the ``input`` field based on the defined ``template_name``. The
  25. image URL is take from *value* and is provided to the template as
  26. ``image_url`` context variable relative to ``MEDIA_URL``. Further
  27. attributes for the ``input`` element are provide in ``input_attrs`` and
  28. contain parameters specified in *attrs* and *name*.
  29. If *value* contains no valid image URL an empty string will be provided
  30. in the context.
  31. """
  32. final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
  33. if not value or isinstance(value, InMemoryUploadedFile):
  34. # can't display images that aren't stored
  35. image_url = ''
  36. else:
  37. image_url = final_attrs['value'] = force_text(
  38. self._format_value(value))
  39. return render_to_string(self.template_name, Context({
  40. 'input_attrs': flatatt(final_attrs),
  41. 'image_url': image_url,
  42. 'image_id': "%s-image" % final_attrs['id'],
  43. }))
  44. class WYSIWYGTextArea(forms.Textarea):
  45. def __init__(self, *args, **kwargs):
  46. kwargs.setdefault('attrs', {})
  47. kwargs['attrs'].setdefault('class', '')
  48. kwargs['attrs']['class'] += ' wysiwyg'
  49. super(WYSIWYGTextArea, self).__init__(*args, **kwargs)
  50. def datetime_format_to_js_date_format(format):
  51. """
  52. Convert a Python datetime format to a date format suitable for use with JS
  53. date pickers
  54. """
  55. converted = format
  56. replacements = {
  57. '%Y': 'yy',
  58. '%m': 'mm',
  59. '%d': 'dd',
  60. '%H:%M': '',
  61. }
  62. for search, replace in six.iteritems(replacements):
  63. converted = converted.replace(search, replace)
  64. return converted.strip()
  65. def datetime_format_to_js_time_format(format):
  66. """
  67. Convert a Python datetime format to a time format suitable for use with JS
  68. date pickers
  69. """
  70. converted = format
  71. replacements = {
  72. '%Y': '',
  73. '%m': '',
  74. '%d': '',
  75. '%H': 'HH',
  76. '%M': 'mm',
  77. }
  78. for search, replace in six.iteritems(replacements):
  79. converted = converted.replace(search, replace)
  80. converted = re.sub('[-/][^%]', '', converted)
  81. return converted.strip()
  82. def add_js_formats(widget):
  83. """
  84. Set data attributes for date and time format on a widget
  85. """
  86. attrs = {
  87. 'data-dateFormat': datetime_format_to_js_date_format(
  88. widget.format),
  89. 'data-timeFormat': datetime_format_to_js_time_format(
  90. widget.format)
  91. }
  92. widget.attrs.update(attrs)
  93. class DatePickerInput(forms.DateInput):
  94. """
  95. DatePicker input that uses the jQuery UI datepicker. Data attributes are
  96. used to pass the date format to the JS
  97. """
  98. def __init__(self, *args, **kwargs):
  99. super(DatePickerInput, self).__init__(*args, **kwargs)
  100. add_js_formats(self)
  101. class DateTimePickerInput(forms.DateTimeInput):
  102. # Build a widget which uses the locale datetime format but without seconds.
  103. # We also use data attributes to pass these formats to the JS datepicker.
  104. def __init__(self, *args, **kwargs):
  105. include_seconds = kwargs.pop('include_seconds', False)
  106. super(DateTimePickerInput, self).__init__(*args, **kwargs)
  107. if not include_seconds:
  108. self.format = re.sub(':?%S', '', self.format)
  109. add_js_formats(self)
  110. class AdvancedSelect(forms.Select):
  111. """
  112. Customised Select widget that allows a list of disabled values to be passed
  113. to the constructor. Django's default Select widget doesn't allow this so
  114. we have to override the render_option method and add a section that checks
  115. for whether the widget is disabled.
  116. """
  117. def __init__(self, attrs=None, choices=(), disabled_values=()):
  118. self.disabled_values = set(force_text(v) for v in disabled_values)
  119. super(AdvancedSelect, self).__init__(attrs, choices)
  120. def render_option(self, selected_choices, option_value, option_label):
  121. option_value = force_text(option_value)
  122. if option_value in self.disabled_values:
  123. selected_html = mark_safe(' disabled="disabled"')
  124. elif option_value in selected_choices:
  125. selected_html = mark_safe(' selected="selected"')
  126. if not self.allow_multiple_selected:
  127. # Only allow for a single selection.
  128. selected_choices.remove(option_value)
  129. else:
  130. selected_html = ''
  131. return format_html(u'<option value="{0}"{1}>{2}</option>',
  132. option_value,
  133. selected_html,
  134. force_text(option_label))