from datetime import datetime, timedelta from mptt.forms import * # noqa from django.forms import * # noqa from django.forms import Form as BaseForm, ModelForm as BaseModelForm from django.utils import timezone from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ TEXT_BASED_FIELDS = ( CharField, EmailField, FilePathField, URLField ) """ Fields """ class YesNoSwitchBase(TypedChoiceField): def prepare_value(self, value): """normalize bools to binary 1/0 so field works on them too""" return value in (True, 'True', 'true', 1, '1') def clean(self, value): return self.prepare_value(value) def YesNoSwitch(**kwargs): yes_label = kwargs.pop('yes_label', _("Yes")) no_label = kwargs.pop('no_label', _("No")) return YesNoSwitchBase( coerce=int, choices=((1, yes_label), (0, no_label)), widget=RadioSelect(attrs={'class': 'yesno-switch'}), **kwargs) class IsoDateTimeField(DateTimeField): input_formats = ['iso8601'] iso8601_formats = ( "%Y-%m-%dT%H:%M:%S", "%Y-%m-%dT%H:%M:%S+00:00", "%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%dT%H:%M:%S.%f+00:00") def prepare_value(self, value): try: return value.isoformat() except AttributeError: return value def strptime(self, value): for format in self.iso8601_formats: try: return datetime.strptime(value, format) except ValueError: pass else: raise ValueError() def to_python(self, value): """ Validates that the input can be converted to a datetime. Returns a Python datetime.datetime object. """ if value in self.empty_values: return None try: unicode_value = force_text(value, strings_only=True) date = unicode_value[:-6] offset = unicode_value[-6:] local_date = self.strptime(value) if offset and offset[0] in ('-', '+'): tz_offset = timedelta(hours=int(offset[1:3]), minutes=int(offset[4:6])) tz_offset = tz_offset.seconds // 60 if offset[0] == '-': tz_offset *= -1 else: tz_offset = 0 tz_correction = timezone.get_fixed_timezone(tz_offset) return timezone.make_aware(local_date, tz_correction) except (IndexError, TypeError, ValueError) as e: raise ValidationError( self.error_messages['invalid'], code='invalid') """ Forms """ class AutoStripWhitespacesMixin(object): autostrip_exclude = [] def full_clean(self): self.data = self.data.copy() for name, field in self.fields.iteritems(): if (field.__class__ in TEXT_BASED_FIELDS and not name in self.autostrip_exclude): try: self.data[name] = (self.data[name] or '').strip() except KeyError: pass return super(AutoStripWhitespacesMixin, self).full_clean() class Form(AutoStripWhitespacesMixin, BaseForm): pass class ModelForm(AutoStripWhitespacesMixin, BaseModelForm): pass