forms.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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 = ("%Y-%m-%dT%H:%M:%S+00:00", "%Y-%m-%dT%H:%M:%S.%f+00:00")
  31. def prepare_value(self, value):
  32. try:
  33. return value.isoformat()
  34. except AttributeError:
  35. return value
  36. def strptime(self, value):
  37. for format in self.iso8601_formats:
  38. try:
  39. return datetime.strptime(value, format)
  40. except ValueError:
  41. pass
  42. else:
  43. raise ValueError()
  44. def to_python(self, value):
  45. """
  46. Validates that the input can be converted to a datetime. Returns a
  47. Python datetime.datetime object.
  48. """
  49. if value in self.empty_values:
  50. return None
  51. try:
  52. unicode_value = force_text(value, strings_only=True)
  53. date = unicode_value[:-6]
  54. offset = unicode_value[-6:]
  55. local_date = self.strptime(value)
  56. if offset and offset[0] in ('-', '+'):
  57. tz_offset = timedelta(hours=int(offset[1:3]),
  58. minutes=int(offset[4:6]))
  59. tz_offset = tz_offset.seconds // 60
  60. if offset[0] == '-':
  61. tz_offset *= -1
  62. else:
  63. tz_offset = 0
  64. tz_correction = timezone.get_fixed_timezone(tz_offset)
  65. return timezone.make_aware(local_date, tz_correction)
  66. except (IndexError, TypeError, ValueError) as e:
  67. raise ValidationError(
  68. self.error_messages['invalid'], code='invalid')
  69. """
  70. Forms
  71. """
  72. class AutoStripWhitespacesMixin(object):
  73. autostrip_exclude = []
  74. def full_clean(self):
  75. self.data = self.data.copy()
  76. for name, field in self.fields.iteritems():
  77. if (field.__class__ in TEXT_BASED_FIELDS and
  78. not name in self.autostrip_exclude):
  79. try:
  80. self.data[name] = self.data[name].strip()
  81. except KeyError:
  82. pass
  83. return super(AutoStripWhitespacesMixin, self).full_clean()
  84. class Form(AutoStripWhitespacesMixin, BaseForm):
  85. pass
  86. class ModelForm(AutoStripWhitespacesMixin, BaseModelForm):
  87. pass