You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

utils.py 6.0 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. from __future__ import unicode_literals
  2. import json
  3. import sys
  4. from django.conf import settings
  5. from django.core.exceptions import ValidationError # backwards compatibility
  6. from django.utils import six, timezone
  7. from django.utils.encoding import force_text, python_2_unicode_compatible
  8. from django.utils.html import escape, format_html, format_html_join, html_safe
  9. from django.utils.translation import ugettext_lazy as _
  10. try:
  11. from collections import UserList
  12. except ImportError: # Python 2
  13. from UserList import UserList
  14. def pretty_name(name):
  15. """Converts 'first_name' to 'First name'"""
  16. if not name:
  17. return ''
  18. return name.replace('_', ' ').capitalize()
  19. def flatatt(attrs):
  20. """
  21. Convert a dictionary of attributes to a single string.
  22. The returned string will contain a leading space followed by key="value",
  23. XML-style pairs. In the case of a boolean value, the key will appear
  24. without a value. It is assumed that the keys do not need to be
  25. XML-escaped. If the passed dictionary is empty, then return an empty
  26. string.
  27. The result is passed through 'mark_safe' (by way of 'format_html_join').
  28. """
  29. key_value_attrs = []
  30. boolean_attrs = []
  31. for attr, value in attrs.items():
  32. if isinstance(value, bool):
  33. if value:
  34. boolean_attrs.append((attr,))
  35. else:
  36. key_value_attrs.append((attr, value))
  37. return (
  38. format_html_join('', ' {}="{}"', sorted(key_value_attrs)) +
  39. format_html_join('', ' {}', sorted(boolean_attrs))
  40. )
  41. @html_safe
  42. @python_2_unicode_compatible
  43. class ErrorDict(dict):
  44. """
  45. A collection of errors that knows how to display itself in various formats.
  46. The dictionary keys are the field names, and the values are the errors.
  47. """
  48. def as_data(self):
  49. return {f: e.as_data() for f, e in self.items()}
  50. def as_json(self, escape_html=False):
  51. return json.dumps({f: e.get_json_data(escape_html) for f, e in self.items()})
  52. def as_ul(self):
  53. if not self:
  54. return ''
  55. return format_html(
  56. '<ul class="errorlist">{}</ul>',
  57. format_html_join('', '<li>{}{}</li>', ((k, force_text(v)) for k, v in self.items()))
  58. )
  59. def as_text(self):
  60. output = []
  61. for field, errors in self.items():
  62. output.append('* %s' % field)
  63. output.append('\n'.join(' * %s' % e for e in errors))
  64. return '\n'.join(output)
  65. def __str__(self):
  66. return self.as_ul()
  67. @html_safe
  68. @python_2_unicode_compatible
  69. class ErrorList(UserList, list):
  70. """
  71. A collection of errors that knows how to display itself in various formats.
  72. """
  73. def __init__(self, initlist=None, error_class=None):
  74. super(ErrorList, self).__init__(initlist)
  75. if error_class is None:
  76. self.error_class = 'errorlist'
  77. else:
  78. self.error_class = 'errorlist {}'.format(error_class)
  79. def as_data(self):
  80. return ValidationError(self.data).error_list
  81. def get_json_data(self, escape_html=False):
  82. errors = []
  83. for error in self.as_data():
  84. message = list(error)[0]
  85. errors.append({
  86. 'message': escape(message) if escape_html else message,
  87. 'code': error.code or '',
  88. })
  89. return errors
  90. def as_json(self, escape_html=False):
  91. return json.dumps(self.get_json_data(escape_html))
  92. def as_ul(self):
  93. if not self.data:
  94. return ''
  95. return format_html(
  96. '<ul class="{}">{}</ul>',
  97. self.error_class,
  98. format_html_join('', '<li>{}</li>', ((force_text(e),) for e in self))
  99. )
  100. def as_text(self):
  101. return '\n'.join('* %s' % e for e in self)
  102. def __str__(self):
  103. return self.as_ul()
  104. def __repr__(self):
  105. return repr(list(self))
  106. def __contains__(self, item):
  107. return item in list(self)
  108. def __eq__(self, other):
  109. return list(self) == other
  110. def __ne__(self, other):
  111. return list(self) != other
  112. def __getitem__(self, i):
  113. error = self.data[i]
  114. if isinstance(error, ValidationError):
  115. return list(error)[0]
  116. return force_text(error)
  117. def __reduce_ex__(self, *args, **kwargs):
  118. # The `list` reduce function returns an iterator as the fourth element
  119. # that is normally used for repopulating. Since we only inherit from
  120. # `list` for `isinstance` backward compatibility (Refs #17413) we
  121. # nullify this iterator as it would otherwise result in duplicate
  122. # entries. (Refs #23594)
  123. info = super(UserList, self).__reduce_ex__(*args, **kwargs)
  124. return info[:3] + (None, None)
  125. # Utilities for time zone support in DateTimeField et al.
  126. def from_current_timezone(value):
  127. """
  128. When time zone support is enabled, convert naive datetimes
  129. entered in the current time zone to aware datetimes.
  130. """
  131. if settings.USE_TZ and value is not None and timezone.is_naive(value):
  132. current_timezone = timezone.get_current_timezone()
  133. try:
  134. return timezone.make_aware(value, current_timezone)
  135. except Exception:
  136. message = _(
  137. '%(datetime)s couldn\'t be interpreted '
  138. 'in time zone %(current_timezone)s; it '
  139. 'may be ambiguous or it may not exist.'
  140. )
  141. params = {'datetime': value, 'current_timezone': current_timezone}
  142. six.reraise(ValidationError, ValidationError(
  143. message,
  144. code='ambiguous_timezone',
  145. params=params,
  146. ), sys.exc_info()[2])
  147. return value
  148. def to_current_timezone(value):
  149. """
  150. When time zone support is enabled, convert aware datetimes
  151. to naive datetimes in the current time zone for display.
  152. """
  153. if settings.USE_TZ and value is not None and timezone.is_aware(value):
  154. current_timezone = timezone.get_current_timezone()
  155. return timezone.make_naive(value, current_timezone)
  156. return value