Making a SplitDurationWidget for Django

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.