1

TL;DR: I want to add minutes to a datetime in postgres and can think of two ways to do it.

Consider the following django model:

from django.db import models


class AppointmentManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().annotate(
            end=models.ExpressionWrapper(
                models.F('start') + models.F('duration'),
                output_field=models.DateTimeField(),
            )
        )


class Appointment(models.Model):
    start = models.DateTimeField()
    duration = models.DurationField()

    objects = AppointmentManager()

Note that end is computed dynamically in the database by adding start and duration. This does exactly what I want.

However, I want the UI for the duration to be a number input that gives the duration in minutes. My current approach is to use the following form field:

import datetime
from django import forms


class MinutesField(forms.DurationField):
    widget = forms.NumberInput

    def prepare_value(self, value):
        if isinstance(value, datetime.timedelta):
            return int(value.total_seconds() / 60)
        return value

    def to_python(self, value):
        if value in self.empty_values:
            return None
        if isinstance(value, datetime.timedelta):
            return value
        if isinstance(value, str):
            value = int(value, 10)
        return datetime.timedelta(seconds=value * 60)

This looks a little brittle to me. Is it also possible to store the duration as integer and transform it to a duration dynamically? Something like this:

class AppointmentManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().annotate(
            end=models.ExpressionWrapper(
                models.F('start') + models.Func(models.F('duration'), function='minutes'),
                output_field=models.DateTimeField(),
            )
        )

class Appointment(models.Model):
    start = models.DateTimeField()
    duration = models.IntegerField()

    objects = AppointmentManager()

1 Answer 1

2

Found a solution in this answer:

class AppointmentManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().annotate(
            end=models.ExpressionWrapper(
                models.F('start') + datetime.timedelta(seconds=60) * models.F('duration'),
                output_field=models.DateTimeField(),
            )
        )
Sign up to request clarification or add additional context in comments.

1 Comment

This currently only works for postgres though. see code.djangoproject.com/ticket/25287

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.