utils.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. from __future__ import unicode_literals
  2. import json
  3. import sys
  4. import warnings
  5. try:
  6. from collections import UserList
  7. except ImportError: # Python 2
  8. from UserList import UserList
  9. from django.conf import settings
  10. from django.utils.deprecation import RemovedInDjango18Warning
  11. from django.utils.encoding import force_text, python_2_unicode_compatible
  12. from django.utils.html import format_html, format_html_join, escape
  13. from django.utils import timezone
  14. from django.utils.translation import ugettext_lazy as _
  15. from django.utils import six
  16. # Import ValidationError so that it can be imported from this
  17. # module to maintain backwards compatibility.
  18. from django.core.exceptions import ValidationError
  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. It is assumed that the keys do not need to be XML-escaped.
  24. If the passed dictionary is empty, then return an empty string.
  25. The result is passed through 'mark_safe'.
  26. """
  27. for attr_name, value in attrs.items():
  28. if type(value) is bool:
  29. warnings.warn(
  30. "In Django 1.8, widget attribute %(attr_name)s=%(bool_value)s "
  31. "will %(action)s. To preserve current behavior, use the "
  32. "string '%(bool_value)s' instead of the boolean value." % {
  33. 'attr_name': attr_name,
  34. 'action': "be rendered as '%s'" % attr_name if value else "not be rendered",
  35. 'bool_value': value,
  36. },
  37. RemovedInDjango18Warning
  38. )
  39. return format_html_join('', ' {0}="{1}"', sorted(attrs.items()))
  40. @python_2_unicode_compatible
  41. class ErrorDict(dict):
  42. """
  43. A collection of errors that knows how to display itself in various formats.
  44. The dictionary keys are the field names, and the values are the errors.
  45. """
  46. def as_data(self):
  47. return {f: e.as_data() for f, e in self.items()}
  48. def as_json(self, escape_html=False):
  49. return json.dumps({f: e.get_json_data(escape_html) for f, e in self.items()})
  50. def as_ul(self):
  51. if not self:
  52. return ''
  53. return format_html(
  54. '<ul class="errorlist">{0}</ul>',
  55. format_html_join('', '<li>{0}{1}</li>', ((k, force_text(v)) for k, v in self.items()))
  56. )
  57. def as_text(self):
  58. output = []
  59. for field, errors in self.items():
  60. output.append('* %s' % field)
  61. output.append('\n'.join(' * %s' % e for e in errors))
  62. return '\n'.join(output)
  63. def __str__(self):
  64. return self.as_ul()
  65. @python_2_unicode_compatible
  66. class ErrorList(UserList, list):
  67. """
  68. A collection of errors that knows how to display itself in various formats.
  69. """
  70. def as_data(self):
  71. return ValidationError(self.data).error_list
  72. def get_json_data(self, escape_html=False):
  73. errors = []
  74. for error in self.as_data():
  75. message = list(error)[0]
  76. errors.append({
  77. 'message': escape(message) if escape_html else message,
  78. 'code': error.code or '',
  79. })
  80. return errors
  81. def as_json(self, escape_html=False):
  82. return json.dumps(self.get_json_data(escape_html))
  83. def as_ul(self):
  84. if not self.data:
  85. return ''
  86. return format_html(
  87. '<ul class="errorlist">{0}</ul>',
  88. format_html_join('', '<li>{0}</li>', ((force_text(e),) for e in self))
  89. )
  90. def as_text(self):
  91. return '\n'.join('* %s' % e for e in self)
  92. def __str__(self):
  93. return self.as_ul()
  94. def __repr__(self):
  95. return repr(list(self))
  96. def __contains__(self, item):
  97. return item in list(self)
  98. def __eq__(self, other):
  99. return list(self) == other
  100. def __ne__(self, other):
  101. return list(self) != other
  102. def __getitem__(self, i):
  103. error = self.data[i]
  104. if isinstance(error, ValidationError):
  105. return list(error)[0]
  106. return force_text(error)
  107. # Utilities for time zone support in DateTimeField et al.
  108. def from_current_timezone(value):
  109. """
  110. When time zone support is enabled, convert naive datetimes
  111. entered in the current time zone to aware datetimes.
  112. """
  113. if settings.USE_TZ and value is not None and timezone.is_naive(value):
  114. current_timezone = timezone.get_current_timezone()
  115. try:
  116. return timezone.make_aware(value, current_timezone)
  117. except Exception:
  118. message = _(
  119. '%(datetime)s couldn\'t be interpreted '
  120. 'in time zone %(current_timezone)s; it '
  121. 'may be ambiguous or it may not exist.'
  122. )
  123. params = {'datetime': value, 'current_timezone': current_timezone}
  124. six.reraise(ValidationError, ValidationError(
  125. message,
  126. code='ambiguous_timezone',
  127. params=params,
  128. ), sys.exc_info()[2])
  129. return value
  130. def to_current_timezone(value):
  131. """
  132. When time zone support is enabled, convert aware datetimes
  133. to naive dateimes in the current time zone for display.
  134. """
  135. if settings.USE_TZ and value is not None and timezone.is_aware(value):
  136. current_timezone = timezone.get_current_timezone()
  137. return timezone.make_naive(value, current_timezone)
  138. return value