Django's
DurationField lets you store a Python timedelta object in the database.
It was introduced in Django 1.8, and doesn't yet have a user-friendly
way to input this data in a form. There's
forms.DurationField, which renders a
<input type="text">
that accepts any string
understood by
parse_duration.
These are my first attempts to make a re-usable MultiValueField that lets users specify the duration in days, hours, minutes, and seconds.
class SplitDurationWidget(forms.MultiWidget): """ A Widget that splits duration input into four number input boxes. """ def __init__(self, attrs=None): widgets = (forms.NumberInput(attrs=attrs), forms.NumberInput(attrs=attrs), forms.NumberInput(attrs=attrs), forms.NumberInput(attrs=attrs)) super(SplitDurationWidget, self).__init__(widgets, attrs) def decompress(self, value): if value: d = value if d: hours = d.seconds // 3600 minutes = (d.seconds % 3600) // 60 seconds = d.seconds % 60 return [int(d.days), int(hours), int(minutes), int(seconds)] return [0, 1, 0, 0] class MultiValueDurationField(forms.MultiValueField): widget = SplitDurationWidget def __init__(self, *args, **kwargs): fields = ( forms.IntegerField(), forms.IntegerField(), forms.IntegerField(), forms.IntegerField(), ) super(MultiValueDurationField, self).__init__( fields=fields, require_all_fields=True, *args, **kwargs) def compress(self, data_list): if len(data_list) == 4: return timedelta( days=int(data_list[0]), hours=int(data_list[1]), minutes=int(data_list[2]), seconds=int(data_list[3])) else: return timedelta(0)
And overrode the field type in the form:
class ItemUpdateForm(ModelForm): def __init__(self, *args, **kwargs): super(ItemUpdateForm, self).__init__(*args, **kwargs) self.fields['estimated_time'] = MultiValueDurationField() class Meta: model = Item
The tests can be written using the same methods used to test, for example, SplitDateTimeWidget in Django.
But then, maybe we should instead just use simpleduration, since users are already familiar with that format in other parts of dmt like the time tracker.