﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
27473	Allow using Extract() with DurationField	Daniel Hahler	nobody	"I am trying to use `Extract` on a duration field, but this results in `ValueError: Extract input expression must be DateField, DateTimeField, or TimeField.`:

{{{
qs = qs.annotate(
    speed=Sum(
        F('distance_in_meters') * Value(3.6) /
            Extract(F('duration_in_seconds'), 'epoch'),
        output_field=FloatField()),
)
}}}


The following patch however makes it work on PostgreSQL, so it should be considered to be enabled there - in case other DB backends do not support it:

{{{
diff --git i/django/db/models/functions/datetime.py w/django/db/models/functions/datetime.py
index 2980460..3266ef6 100644
--- i/django/db/models/functions/datetime.py
+++ w/django/db/models/functions/datetime.py
@@ -5,6 +5,7 @@
 from django.conf import settings
 from django.db.models import (
     DateField, DateTimeField, IntegerField, TimeField, Transform,
+    DurationField,
 )
 from django.db.models.lookups import (
     YearExact, YearGt, YearGte, YearLt, YearLte,
@@ -52,6 +53,8 @@ def as_sql(self, compiler, connection):
             sql = connection.ops.date_extract_sql(self.lookup_name, sql)
         elif isinstance(lhs_output_field, TimeField):
             sql = connection.ops.time_extract_sql(self.lookup_name, sql)
+        elif isinstance(lhs_output_field, DurationField):
+            sql = connection.ops.date_extract_sql(self.lookup_name, sql)
         else:
             # resolve_expression has already validated the output_field so this
             # assert should never be hit.
@@ -61,7 +64,7 @@ def as_sql(self, compiler, connection):
     def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
         copy = super(Extract, self).resolve_expression(query, allow_joins, reuse, summarize, for_save)
         field = copy.lhs.output_field
-        if not isinstance(field, (DateField, DateTimeField, TimeField)):
+        if not isinstance(field, (DateField, DateTimeField, TimeField, DurationField)):
             raise ValueError('Extract input expression must be DateField, DateTimeField, or TimeField.')
         # Passing dates to functions expecting datetimes is most likely a mistake.
         if type(field) == DateField and copy.lookup_name in ('hour', 'minute', 'second'):
}}}

Am I missing something?
What needs to be done to get it enabled for DurationFields?
"	New feature	closed	Database layer (models, ORM)	1.10	Normal	fixed		Josh Smeaton	Accepted	1	0	0	0	0	0
