n0rman0
n0rman0

Reputation: 113

Subclass timedelta within Django model field

I have extended datetime.timedelta for some previous work to update the division operator to support floats (not supported before python 3.x). Having started using a DurationField in one of my models I would like to create a custom version which uses my extended timedelta class so that objects returned after a query include my custom timedelta class. What is the correct approach to implement this?

I've been editing the DurationField class itself whilst I figure out how to do the above before taking the next step to figure out how to add a custom model field. Code as follows (modified Django source code:

class TimeDeltaExtended(datetime.timedelta):
    def __div__(self, divisor):
        return TimeDeltaExtended(seconds=self.total_seconds()/divisor)

    def __mul__(self, multiplier):
        return TimeDeltaExtended(seconds=self.total_seconds()*multiplier)

    def __add__(self, other):
        if isinstance(other, int) and other is 0:
            return self
        else:
            datetime.timedelta.__add__(other)

class DurationField(Field):
...

    def to_python(self, value):
        if value is None:
            return value
        if isinstance(value, TimeDeltaExtended):
            return value
        try:
            parsed = parse_duration(value)
        except ValueError:
            pass
        else:
            if parsed is not None:
                return parsed

        raise exceptions.ValidationError(
            self.error_messages['invalid'],
            code='invalid',
            params={'value': value},
        )
...

However when I use this code queries still return normal TimeDelta objects. Is this even the area I should be looking at?

Environment Python 2.7, Django 1.11

Upvotes: 2

Views: 537

Answers (1)

wim
wim

Reputation: 362487

This is how duration field will parse the serialised form of timedelta(seconds=123.456):

>>> DurationField().to_python('00:02:03.456000')
datetime.timedelta(0, 123, 456000)

You can just let the superclass do the heavy lifting of validation, exception handling, etc. Just convert to your specialised class as a post-processing step:

>>> class MyDurationField(DurationField):
...     def to_python(self, value):
...         timedelta = super(MyDurationField, self).to_python(value)
...         return TimeDeltaExtended(seconds=timedelta.total_seconds())
...     
>>> MyDurationField().to_python('00:02:03.456000')
TimeDeltaExtended(0, 123, 456000)
>>> MyDurationField().to_python('00:02:03.456000') / 2.0
TimeDeltaExtended(0, 61, 728000)

Upvotes: 1

Related Questions