forms.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. from datetime import datetime, timedelta
  2. from mptt.forms import * # noqa
  3. from django.forms import * # noqa
  4. from django.forms import Form as BaseForm, ModelForm as BaseModelForm
  5. from django.utils import timezone
  6. from django.utils.encoding import force_text
  7. from django.utils.translation import ugettext_lazy as _
  8. TEXT_BASED_FIELDS = (
  9. CharField, EmailField, FilePathField, URLField
  10. )
  11. """
  12. Fields
  13. """
  14. class YesNoSwitchBase(TypedChoiceField):
  15. def prepare_value(self, value):
  16. """normalize bools to binary 1/0 so field works on them too"""
  17. return 1 if value in [True, 'True', 1, '1'] else 0
  18. def clean(self, value):
  19. return self.prepare_value(value)
  20. def YesNoSwitch(**kwargs):
  21. yes_label = kwargs.pop('yes_label', _("Yes"))
  22. no_label = kwargs.pop('no_label', _("No"))
  23. return YesNoSwitchBase(
  24. coerce=int,
  25. choices=((1, yes_label), (0, no_label)),
  26. widget=RadioSelect(attrs={'class': 'yesno-switch'}),
  27. **kwargs)
  28. class IsoDateTimeField(DateTimeField):
  29. input_formats = ['iso8601']
  30. iso8601_formats = (
  31. "%Y-%m-%dT%H:%M:%S",
  32. "%Y-%m-%dT%H:%M:%S+00:00",
  33. "%Y-%m-%dT%H:%M:%S.%f",
  34. "%Y-%m-%dT%H:%M:%S.%f+00:00")
  35. def prepare_value(self, value):
  36. try:
  37. return value.isoformat()
  38. except AttributeError:
  39. return value
  40. def strptime(self, value):
  41. for format in self.iso8601_formats:
  42. try:
  43. return datetime.strptime(value, format)
  44. except ValueError:
  45. pass
  46. else:
  47. raise ValueError()
  48. def to_python(self, value):
  49. """
  50. Validates that the input can be converted to a datetime. Returns a
  51. Python datetime.datetime object.
  52. """
  53. if value in self.empty_values:
  54. return None
  55. try:
  56. unicode_value = force_text(value, strings_only=True)
  57. date = unicode_value[:-6]
  58. offset = unicode_value[-6:]
  59. local_date = self.strptime(value)
  60. if offset and offset[0] in ('-', '+'):
  61. tz_offset = timedelta(hours=int(offset[1:3]),
  62. minutes=int(offset[4:6]))
  63. tz_offset = tz_offset.seconds // 60
  64. if offset[0] == '-':
  65. tz_offset *= -1
  66. else:
  67. tz_offset = 0
  68. tz_correction = timezone.get_fixed_timezone(tz_offset)
  69. return timezone.make_aware(local_date, tz_correction)
  70. except (IndexError, TypeError, ValueError) as e:
  71. raise ValidationError(
  72. self.error_messages['invalid'], code='invalid')
  73. """
  74. Forms
  75. """
  76. class AutoStripWhitespacesMixin(object):
  77. autostrip_exclude = []
  78. def full_clean(self):
  79. self.data = self.data.copy()
  80. for name, field in self.fields.iteritems():
  81. if (field.__class__ in TEXT_BASED_FIELDS and
  82. not name in self.autostrip_exclude):
  83. try:
  84. self.data[name] = self.data[name].strip()
  85. except KeyError:
  86. pass
  87. return super(AutoStripWhitespacesMixin, self).full_clean()
  88. class Form(AutoStripWhitespacesMixin, BaseForm):
  89. pass
  90. class ModelForm(AutoStripWhitespacesMixin, BaseModelForm):
  91. pass