Ticket #2443: durationfield.4.diff

File durationfield.4.diff, 6.5 KB (added by Marty Alchin <gulopine@…>, 8 years ago)

New patch, complete with documentation

  • django/db/models/fields/__init__.py

     
    636676        defaults.update(kwargs)
    637677        return super(DecimalField, self).formfield(**defaults)
    638678
     679class DurationField(Field):
     680    max_digits, decimal_places = 20, 6
     681
     682    def get_internal_type(self):
     683        return "DecimalField"
     684
     685    def contribute_to_class(self, cls, name):
     686        super(DurationField, self).contribute_to_class(cls, name)
     687        setattr(cls, name, self.lazy_attribute(datetime.timedelta, self.create))
     688
     689    def get_db_prep_save(self, value):
     690        return str(value.days * 24 * 3600 + value.seconds + float(value.microseconds) / 1000000)
     691
     692    def to_python(self, value):
     693        if isinstance(value, datetime.timedelta):
     694            return value
     695        try:
     696            return datetime.timedelta(seconds=float(value))
     697        except (TypeError, ValueError):
     698            raise validators.ValidationError('This value must be a real number.')
     699        except OverflowError:
     700            raise validators.ValidationError('The maximum allowed value is %s' % datetime.timedelta.max)
     701
     702    def create(self, value):
     703        return datetime.timedelta(seconds=float(value) or 0)
     704   
     705    def flatten_data(self, follow, obj=None):
     706        val = self._get_val_from_obj(obj)
     707        if val is None or val is '':
     708            return ''
     709        return {self.attname: self.get_db_prep_save(val)}
     710
     711    def formfield(self, form_class=forms.SplitDurationField, **kwargs):
     712        return super(DurationField, self).formfield(form_class, **kwargs)
     713
     714    def get_manipulator_field_objs(self):
     715        return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
     716
    639717class EmailField(CharField):
    640718    def __init__(self, *args, **kwargs):
    641719        kwargs['maxlength'] = 75
  • django/newforms/fields.py

     
    1010from django.utils.encoding import smart_unicode
    1111
    1212from util import ErrorList, ValidationError
    13 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple
     13from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, SplitDurationWidget
    1414
    1515__all__ = (
    1616    'Field', 'CharField', 'IntegerField',
     
    2020    'RegexField', 'EmailField', 'URLField', 'BooleanField',
    2121    'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
    2222    'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
    23     'SplitDateTimeField',
     23    'SplitDateTimeField', 'SplitDurationField',
    2424)
    2525
    2626# These values, if given to to_python(), will trigger the self.required check.
     
    560560        if data_list:
    561561            return datetime.datetime.combine(*data_list)
    562562        return None
     563
     564class SplitDurationField(MultiValueField):
     565    widget = SplitDurationWidget
     566
     567    def __init__(self, *args, **kwargs):
     568        fields = (
     569            IntegerField(label='Days', max_value=999999999, min_value=-999999999),
     570            IntegerField(label='Hours', max_value=23, min_value=0),
     571            IntegerField(label='Minutes', max_value=59, min_value=0),
     572            IntegerField(label='Seconds', max_value=59, min_value=0),
     573            IntegerField(label='Microseconds', max_value=999999, min_value=0),
     574        )
     575        super(SplitDurationField, self).__init__(fields, *args, **kwargs)
     576
     577    def compress(self, data_list):
     578        if data_list == [None] * 5:
     579            raise ValidationError(gettext(u'This field is required.'))
     580        if data_list:
     581            return datetime.timedelta(
     582                days=data_list[0] or 0,
     583                hours=data_list[1] or 0,
     584                minutes=data_list[2] or 0,
     585                seconds=data_list[3] or 0,
     586                microseconds=data_list[4] or 0,
     587            )
     588        return None
  • django/newforms/widgets.py

     
    2121    'FileInput', 'Textarea', 'CheckboxInput',
    2222    'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
    2323    'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget',
     24    'SplitDurationWidget',
    2425)
    2526
    2627class Widget(object):
     
    374375        if value:
    375376            return [value.date(), value.time()]
    376377        return [None, None]
     378
     379class SplitDurationWidget(MultiWidget):
     380    def __init__(self, attrs=None):
     381        attrs = attrs or {}
     382        widgets = (
     383            TextInput(attrs=dict(attrs, size=4, maxlength=10, title='Days')),
     384            TextInput(attrs=dict(attrs, size=1, maxlength=2, title='Hours')),
     385            TextInput(attrs=dict(attrs, size=1, maxlength=2, title='Minutes')),
     386            TextInput(attrs=dict(attrs, size=1, maxlength=2, title='Seconds')),
     387            TextInput(attrs=dict(attrs, size=5, maxlength=6, title='Microseconds')),
     388        )
     389        super(SplitDurationWidget, self).__init__(widgets, attrs)
     390
     391    def decompress(self, value):
     392        if value:
     393            hours, seconds = divmod(value.seconds, 3600)
     394            minutes, seconds = divmod(seconds, 60)
     395            return [value.days, hours, minutes, seconds, value.microseconds]
     396        return [None, None, None, None, None]
     397
     398    def format_output(self, rendered_widgets):
     399        return u'%s days, %s : %s : %s . %s' % tuple(rendered_widgets)
  • docs/model-api.txt

     
    213213
    214214The admin represents this as an ``<input type="text">`` (a single-line input).
    215215
     216``DurationField``
     217~~~~~~~~~~~~~~~~~
     218
     219**New in Django development version**
     220
     221A span of time, represented in Python by a ``timedelta`` instance.
     222
     223The admin represents this as an ``<input type="text">`` (a single-line input),
     224with its value representing the number of seconds in the duration. Fractional
     225values are allowed, with a millisecond precision.
     226
    216227``EmailField``
    217228~~~~~~~~~~~~~~
    218229
Back to Top