2

I am trying to query a database table in django with, among others, the following columns:

id | start_time | end_time

Rather than getting the separate values for the two, can I just get the difference directly in a query? Something to this effect:

SELECT id, Diff(start_time, end_time) FROM myTable

4 Answers 4

7

This can be done entirely through the ORM in Django 1.10 and above. With a model like so:

class Timer(models.Model)

    start_time = models.DateTimeField()
    end_time = models.DateTimeField()

You can annotate the objects with the duration like so:

from django.db.models import DurationField, ExpressionWrapper, F

Timer.objects.annotate(duration=ExpressionWrapper(F('end_time') - F('start_time'),
                                                  output_field=DurationField()))
Sign up to request clarification or add additional context in comments.

2 Comments

Starting with Django 1.10, this should be the preferred way to achieve what the OP is asking. Alternatively, you can skip the ExpressionWrapper and go with simply Timer.objects.annotate(duration=F('end_time') - F('start_time')). In this case, the duration will contain the difference between end_time and start_time in microseconds.
except if you have timezone information, where this won't work without ExpressionWrapper and DurationField
2

QuerySet.extra() will allow you to specify arbitrary expressions for a column. Note that the result will be DB-dependent.

1 Comment

Since asking this question I have changed my table design and I just used raw SQL anyway; nonetheless I feel that this is exactly the gem of information I was looking for in the django docs and could not find! Thank You!
1

It seems to me that the easiest way is to add a third column with the difference and update it every time the object is modified like this:

class Timer(models.Model)

    start_time = models.DateTimeField()
    end_time = models.DateTimeField()
    diff_time = models.DateTimeField()

    def __init__(self, *args, **kwargs):
        super(Timer, self).__init__(*args, **kwargs)
        self.diff_time = self.end_time - self.start_time

    def save(self, *args, **kwargs):
        self.diff_time = self.end_time - self.start_time
        super(Timer, self).save(*args, **kwargs)

I tried to look for a solution with annotate, but there is no support for aggregate functions in either mysql nor postgres. Even if there where, seem like nice trade off an extra column for not having to calculate the diff for each row on each query!

Oh, and you can just retrieve what you want like this:

 Timer.objects.values('id', 'diff_time')

3 Comments

That just seems like bad table design to me. Why add a column to my table that holds redundant data? Not only is it silly from a data modeling standpoint to hold redundant data but this solution also reduces the scalability of my application.
I have to agree with the modeling point of view, it´s a desnormalization (A fact that is not hidden at all).But from scalability point of view, I think you are wrong. Whenever you do queries using that diff, they´ll be much more faster, which is what scalability all about.
You're right, the queries would indeed be faster. There would just be an added overhead when I'm storing my data into the tables, but single record insertion overhead would clearly be less "harmful" than the overhead for processing the entire table's content. The user won't even notice the delay when they're creating the data.
1

As of Django 1.8, extra is discouraged in favor of annotate+RawSQL. The following works for MySQL:

res = MyTable.objects.annotate(duration=RawSQL('datediff(endtime, starttime)', ()))
# examine the results
print res.values_list('duration', 'starttime', 'endtime')

Comments

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.