#27473 closed New feature (fixed)
Allow using Extract() with DurationField
Reported by: | Daniel Hahler | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.10 |
Severity: | Normal | Keywords: | |
Cc: | Josh Smeaton | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
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?
Change History (8)
comment:1 by , 8 years ago
Cc: | added |
---|---|
Summary: | Use/enable db.models.functions.Extract with/for DurationFields → Allow using Extract() with DurationField |
Triage Stage: | Unreviewed → Accepted |
comment:2 by , 8 years ago
Cool, working on a PR now.
I've noticed that with SQLite, the "Extract" for a DurationField appears to return NULL/None always, e.g. with DTModel.objects.annotate(duration_s=Extract('duration', 'second'))
.
Should this throw some error instead? Would that be possible in Extract
already (so basically keeping the current behavior there for unsupported backends)?
Or would it need some other special casing?
comment:4 by , 8 years ago
Patch needs improvement: | set |
---|
comment:5 by , 7 years ago
Patch needs improvement: | unset |
---|
I'm not sure, Josh might have some advice. If we have a tested patch, we can go from there.