#35344 closed Bug (fixed)
GeneratedField get_col output_field bug
Reported by: | Johannes Westphal | Owned by: | Johannes Westphal |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Release blocker | Keywords: | |
Cc: | Johannes Westphal | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Related issue #34838.
For generated fields, get_col
gets called with output_field=<instance of the generated field>
. Consequently, if an alias
is specified, the output_field
of the generated Col
is of type GeneratedField
instead of the actual output_field
of the GeneratedField
, since the current code only handles the case where get_col
's output_field
parameter is None
, but not when it is self
.
Error
File "django/contrib/postgres/fields/ranges.py", line 253, in as_postgresql cast_internal_type = self.lhs.output_field.base_field.get_internal_type() AttributeError: 'GeneratedField' object has no attribute 'base_field'
Patch
-
django/db/models/fields/generated.py
diff --git a/django/db/models/fields/generated.py b/django/db/models/fields/generated.py index 257feeeba2..5b6b188df0 100644
a b class GeneratedField(Field): 39 39 return Col(self.model._meta.db_table, self, self.output_field) 40 40 41 41 def get_col(self, alias, output_field=None): 42 if alias != self.model._meta.db_table and output_field i s None:42 if alias != self.model._meta.db_table and output_field in (None, self): 43 43 output_field = self.output_field 44 44 return super().get_col(alias, output_field)
Change History (12)
comment:1 by , 9 months ago
comment:2 by , 9 months ago
Version: | 5.0 → dev |
---|
comment:3 by , 9 months ago
Triage Stage: | Unreviewed → Accepted |
---|
Nice catch and thank you for submitting a PR including a regression test, this is brilliant!
Pending a mention in the 5.0.4 release notes this LGTM!
comment:7 by , 8 months ago
Thank you Simon and Johannes for your help, the PR looks good, I'm completing the review and I'll merge soon.
While I understand how the provided test is relevant for the fix, I would like to understand a bit better the high-level use case. Would you have an example of a query producing the reported error?
comment:8 by , 8 months ago
Triage Stage: | Accepted → Ready for checkin |
---|
comment:9 by , 8 months ago
Here a minimal example to reproduce the crash:
Model
from django.contrib.gis.db import models from django.contrib.postgres.fields import DateTimeRangeField from django.contrib.postgres.functions import TransactionNow class DateTimeRange(models.Func): function = 'tstzrange' template = "%(function)s(%(expressions)s)" output_field = DateTimeRangeField() def __init__(self, a, b): super().__init__(a, b) class P(models.Model): start = models.DateTimeField(db_default=TransactionNow()) finish = models.DateTimeField(null=True) timerange = models.GeneratedField(expression=DateTimeRange(models.F('start'), models.F('finish')), output_field=DateTimeRangeField(), db_persist=True) class T(models.Model): p1 = models.ForeignKey(P, on_delete=models.CASCADE, related_name='tp1s') p2 = models.ForeignKey(P, on_delete=models.CASCADE, related_name='tp2s')
Query
from django.utils import timezone now = timezone.now() T.objects.filter( p1__timerange__contains=now, p2__timerange__contains=now, ).count()
Traceback
Traceback (most recent call last): File "test.py", line 14, in <module> ).count() File "django/db/models/query.py", line 620, in count return self.query.get_count(using=self.db) File "django/db/models/sql/query.py", line 629, in get_count return obj.get_aggregation(using, {"__count": Count("*")})["__count"] File "django/db/models/sql/query.py", line 615, in get_aggregation result = compiler.execute_sql(SINGLE) File "django/db/models/sql/compiler.py", line 1549, in execute_sql sql, params = self.as_sql() File "django/db/models/sql/compiler.py", line 764, in as_sql self.compile(self.where) if self.where is not None else ("", []) File "django/db/models/sql/compiler.py", line 546, in compile sql, params = node.as_sql(self, self.connection) File "django/db/models/sql/where.py", line 151, in as_sql sql, params = compiler.compile(child) File "django/db/models/sql/compiler.py", line 544, in compile sql, params = vendor_impl(self, self.connection) File "django/contrib/postgres/fields/ranges.py", line 253, in as_postgresql cast_internal_type = self.lhs.output_field.base_field.get_internal_type() AttributeError: 'GeneratedField' object has no attribute 'base_field'
comment:12 by , 8 months ago
Thank you Natalia and Simon for the fast processing of my bug report and pull request.
PR https://github.com/django/django/pull/18034.