widgets.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. """
  2. Extra HTML Widget classes
  3. """
  4. from __future__ import unicode_literals
  5. import datetime
  6. import re
  7. from django.forms.widgets import Widget, Select
  8. from django.utils import datetime_safe
  9. from django.utils.dates import MONTHS
  10. from django.utils.encoding import force_str
  11. from django.utils.safestring import mark_safe
  12. from django.utils.formats import get_format
  13. from django.utils import six
  14. from django.conf import settings
  15. __all__ = ('SelectDateWidget',)
  16. RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$')
  17. def _parse_date_fmt():
  18. fmt = get_format('DATE_FORMAT')
  19. escaped = False
  20. output = []
  21. for char in fmt:
  22. if escaped:
  23. escaped = False
  24. elif char == '\\':
  25. escaped = True
  26. elif char in 'Yy':
  27. output.append('year')
  28. #if not self.first_select: self.first_select = 'year'
  29. elif char in 'bEFMmNn':
  30. output.append('month')
  31. #if not self.first_select: self.first_select = 'month'
  32. elif char in 'dj':
  33. output.append('day')
  34. #if not self.first_select: self.first_select = 'day'
  35. return output
  36. class SelectDateWidget(Widget):
  37. """
  38. A Widget that splits date input into three <select> boxes.
  39. This also serves as an example of a Widget that has more than one HTML
  40. element and hence implements value_from_datadict.
  41. """
  42. none_value = (0, '---')
  43. month_field = '%s_month'
  44. day_field = '%s_day'
  45. year_field = '%s_year'
  46. def __init__(self, attrs=None, years=None, months=None):
  47. self.attrs = attrs or {}
  48. # Optional list or tuple of years to use in the "year" select box.
  49. if years:
  50. self.years = years
  51. else:
  52. this_year = datetime.date.today().year
  53. self.years = range(this_year, this_year + 10)
  54. # Optional dict of months to use in the "month" select box.
  55. if months:
  56. self.months = months
  57. else:
  58. self.months = MONTHS
  59. def render(self, name, value, attrs=None):
  60. try:
  61. year_val, month_val, day_val = value.year, value.month, value.day
  62. except AttributeError:
  63. year_val = month_val = day_val = None
  64. if isinstance(value, six.string_types):
  65. if settings.USE_L10N:
  66. try:
  67. input_format = get_format('DATE_INPUT_FORMATS')[0]
  68. v = datetime.datetime.strptime(force_str(value), input_format)
  69. year_val, month_val, day_val = v.year, v.month, v.day
  70. except ValueError:
  71. pass
  72. else:
  73. match = RE_DATE.match(value)
  74. if match:
  75. year_val, month_val, day_val = [int(v) for v in match.groups()]
  76. choices = [(i, i) for i in self.years]
  77. year_html = self.create_select(name, self.year_field, value, year_val, choices)
  78. choices = list(six.iteritems(self.months))
  79. month_html = self.create_select(name, self.month_field, value, month_val, choices)
  80. choices = [(i, i) for i in range(1, 32)]
  81. day_html = self.create_select(name, self.day_field, value, day_val, choices)
  82. output = []
  83. for field in _parse_date_fmt():
  84. if field == 'year':
  85. output.append(year_html)
  86. elif field == 'month':
  87. output.append(month_html)
  88. elif field == 'day':
  89. output.append(day_html)
  90. return mark_safe('\n'.join(output))
  91. def id_for_label(self, id_):
  92. first_select = None
  93. field_list = _parse_date_fmt()
  94. if field_list:
  95. first_select = field_list[0]
  96. if first_select is not None:
  97. return '%s_%s' % (id_, first_select)
  98. else:
  99. return '%s_month' % id_
  100. def value_from_datadict(self, data, files, name):
  101. y = data.get(self.year_field % name)
  102. m = data.get(self.month_field % name)
  103. d = data.get(self.day_field % name)
  104. if y == m == d == "0":
  105. return None
  106. if y and m and d:
  107. if settings.USE_L10N:
  108. input_format = get_format('DATE_INPUT_FORMATS')[0]
  109. try:
  110. date_value = datetime.date(int(y), int(m), int(d))
  111. except ValueError:
  112. return '%s-%s-%s' % (y, m, d)
  113. else:
  114. date_value = datetime_safe.new_date(date_value)
  115. return date_value.strftime(input_format)
  116. else:
  117. return '%s-%s-%s' % (y, m, d)
  118. return data.get(name, None)
  119. def create_select(self, name, field, value, val, choices):
  120. if 'id' in self.attrs:
  121. id_ = self.attrs['id']
  122. else:
  123. id_ = 'id_%s' % name
  124. if not self.is_required:
  125. choices.insert(0, self.none_value)
  126. local_attrs = self.build_attrs(id=field % id_)
  127. s = Select(choices=choices)
  128. select_html = s.render(field % name, val, local_attrs)
  129. return select_html