From 2918c3de74d1abe571b6c1eeb35e2899c39d880e Mon Sep 17 00:00:00 2001 From: Justin Bronn Date: Sat, 10 Sep 2011 22:53:26 +0000 Subject: [PATCH] Fixed #14648 -- Fixed annotated date querysets when `GeoManager` is used. Thanks, codysoyland, for the bug report. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16796 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- .../gis/db/backends/spatialite/compiler.py | 32 ------------------ .../gis/db/backends/spatialite/operations.py | 2 +- django/contrib/gis/db/models/sql/compiler.py | 28 ++++++++++++--- .../relatedapp/fixtures/initial_data.json.gz | Bin 528 -> 526 bytes django/contrib/gis/tests/relatedapp/models.py | 1 + django/contrib/gis/tests/relatedapp/tests.py | 8 +++++ 6 files changed, 34 insertions(+), 37 deletions(-) delete mode 100644 django/contrib/gis/db/backends/spatialite/compiler.py diff --git a/django/contrib/gis/db/backends/spatialite/compiler.py b/django/contrib/gis/db/backends/spatialite/compiler.py deleted file mode 100644 index 3f81ae68a1..0000000000 --- a/django/contrib/gis/db/backends/spatialite/compiler.py +++ /dev/null @@ -1,32 +0,0 @@ -from django.db.backends.util import typecast_timestamp -from django.db.models.sql import compiler -from django.db.models.sql.constants import MULTI -from django.contrib.gis.db.models.sql.compiler import GeoSQLCompiler as BaseGeoSQLCompiler - -SQLCompiler = compiler.SQLCompiler - -class GeoSQLCompiler(BaseGeoSQLCompiler, SQLCompiler): - pass - -class SQLInsertCompiler(compiler.SQLInsertCompiler, GeoSQLCompiler): - pass - -class SQLDeleteCompiler(compiler.SQLDeleteCompiler, GeoSQLCompiler): - pass - -class SQLUpdateCompiler(compiler.SQLUpdateCompiler, GeoSQLCompiler): - pass - -class SQLAggregateCompiler(compiler.SQLAggregateCompiler, GeoSQLCompiler): - pass - -class SQLDateCompiler(compiler.SQLDateCompiler, GeoSQLCompiler): - """ - This is overridden for GeoDjango to properly cast date columns, see #16757. - """ - def results_iter(self): - offset = len(self.query.extra_select) - for rows in self.execute_sql(MULTI): - for row in rows: - date = typecast_timestamp(str(row[offset])) - yield date diff --git a/django/contrib/gis/db/backends/spatialite/operations.py b/django/contrib/gis/db/backends/spatialite/operations.py index 2ba54e5228..449c527187 100644 --- a/django/contrib/gis/db/backends/spatialite/operations.py +++ b/django/contrib/gis/db/backends/spatialite/operations.py @@ -48,7 +48,7 @@ def get_dist_ops(operator): return (SpatiaLiteDistance(operator),) class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations): - compiler_module = 'django.contrib.gis.db.backends.spatialite.compiler' + compiler_module = 'django.contrib.gis.db.models.sql.compiler' name = 'spatialite' spatialite = True version_regex = re.compile(r'^(?P\d)\.(?P\d)\.(?P\d+)') diff --git a/django/contrib/gis/db/models/sql/compiler.py b/django/contrib/gis/db/models/sql/compiler.py index 782ce78368..405a000f42 100644 --- a/django/contrib/gis/db/models/sql/compiler.py +++ b/django/contrib/gis/db/models/sql/compiler.py @@ -1,7 +1,7 @@ from itertools import izip -from django.db.backends.util import truncate_name +from django.db.backends.util import truncate_name, typecast_timestamp from django.db.models.sql import compiler -from django.db.models.sql.constants import TABLE_NAME +from django.db.models.sql.constants import TABLE_NAME, MULTI from django.db.models.sql.query import get_proxied_model SQLCompiler = compiler.SQLCompiler @@ -194,7 +194,7 @@ class GeoSQLCompiler(compiler.SQLCompiler): # We resolve the rest of the columns if we're on Oracle or if # the `geo_values` attribute is defined. for value, field in map(None, row[index_start:], fields): - values.append(self.query.convert_values(value, field, connection=self.connection)) + values.append(self.query.convert_values(value, field, self.connection)) else: values.extend(row[index_start:]) return tuple(values) @@ -275,4 +275,24 @@ class SQLAggregateCompiler(compiler.SQLAggregateCompiler, GeoSQLCompiler): pass class SQLDateCompiler(compiler.SQLDateCompiler, GeoSQLCompiler): - pass + """ + This is overridden for GeoDjango to properly cast date columns, since + `GeoQuery.resolve_columns` is used for spatial values. + See #14648, #16757. + """ + def results_iter(self): + if self.connection.ops.oracle: + from django.db.models.fields import DateTimeField + fields = [DateTimeField()] + else: + needs_string_cast = self.connection.features.needs_datetime_string_cast + + offset = len(self.query.extra_select) + for rows in self.execute_sql(MULTI): + for row in rows: + date = row[offset] + if self.connection.ops.oracle: + date = self.resolve_columns(row, fields)[offset] + elif needs_string_cast: + date = typecast_timestamp(str(date)) + yield date diff --git a/django/contrib/gis/tests/relatedapp/fixtures/initial_data.json.gz b/django/contrib/gis/tests/relatedapp/fixtures/initial_data.json.gz index 68bf54c1b0d196a4e3b35d1b9045a6d8bf3f416e..893763724b819f615c09dd01e1b5705f5352d4c6 100644 GIT binary patch literal 526 zcmV+p0`dJHiwFSQ&TCEp1GUsoZ<{a_0PuT1#S^DZDhS&c$fg~VMpL6HUDP6_)wF{X zyny0hWK1_S<-5-$T^dz{5vCllK0Cc^tY-G%ZsauK*rg_Zr z+!1ogbSg9JWz3J1i?}d@_6yWY$TZXStcUx__>)Jl|8e$pau>lndk{Dj`3NB(#Bpgz z2zhVq=50;6y?*1smvSK2b0`j7>emnw1{mWu2)Ps_7&xBq2E+r;bx`O9ejCHP*t<}N z_ykb|c;Ey+A%WNCpcixaPUzMF0t7ojh+P`AIojfMnd<7aA!8d}PZ*X;D)utYv=`dU zuREg0*T%z<%#QtnwHxMM7C*TV4Vt@YqXzej*lwCHe7PffM9@n@-E z##VwKT~RC-buStOo=t5`tW$=zKdGx>o(hq&jX`KtH5jOCBO9G0v|YnU%Jpf0PPN$I zf_afrafsNj^cBGN{-aEC&gnaDJ%JlpiWnl3P2fqEFe)`f3D0(w`L6GlJ#)|!797UB z&`MT3hD8Naeh#)R&2+?$1k2X&kV{>F%s?j`rYt`P(Uw~}B6eX?Z+|26N~NnrHvkUl Qc6;^x4;-0u?=uMi0Q%YZ6#xJL literal 528 zcmV+r0`L7FiwFo=x%Nx|18Ht)bZKF1Uu0o)VJ>QOZ*Bm!lwE7vFcgOG`zsE<+MpA* zB54>6BVA|>SsA-fVl6gNEF;U^Ov!(roOEd^j;46zLIQmpz0Z+eo$nU_Pr3m( z<=VLd?{Yubh?H?Xb|m9ev6Pl2;$lfvE(+&)&(ECkw9=dQ2ee0syije_{oQ2z$tO7c zIQu%eP2k;!L4*P?^gIs;L2eKeLf$*OxUKHDmv1=mxi~!6M*)uAAZ$TM9AS)2Amm&i z(E$0O8xbFT7kRNCg(eLDZ0`~v5)v=)01r?U5)%0)j^Jz@A&TABAV4sRV(bQyiNoa6 zWv-gz3?>TCO^HX2VV%($BhfJY&i^t?ZvF=;vQMm>W@7>R@sU8vid%3-g*K{BM@eLMoW+ zF6(hQZ*fdtYFMjG$bSPRl06CuGrHoeaE>XN{)?^BJm);88!e?|b5zNc(@2Q*aUGQ_ zzE4SCZ!H-l^mqPpp>&V=sw|w6q^0x(u0_pLNOWm{2T{YQR*+<@*bU~pwHx}#KnYkd z7_&-A(XbR24NTc7u%?<}2|MDnSi^lOR0W~{l`)vo@)QVDp|FJ5xuKQ67G)#zRi-)s S4z+W8@#7D)DyrK62><{}zVY(_ diff --git a/django/contrib/gis/tests/relatedapp/models.py b/django/contrib/gis/tests/relatedapp/models.py index 2e9a62b61f..aec4e15749 100644 --- a/django/contrib/gis/tests/relatedapp/models.py +++ b/django/contrib/gis/tests/relatedapp/models.py @@ -36,6 +36,7 @@ class Parcel(models.Model): # These use the GeoManager but do not have any geographic fields. class Author(models.Model): name = models.CharField(max_length=100) + dob = models.DateField() objects = models.GeoManager() class Article(models.Model): diff --git a/django/contrib/gis/tests/relatedapp/tests.py b/django/contrib/gis/tests/relatedapp/tests.py index f51f34398f..685700e9ae 100644 --- a/django/contrib/gis/tests/relatedapp/tests.py +++ b/django/contrib/gis/tests/relatedapp/tests.py @@ -1,3 +1,4 @@ +from datetime import date from django.test import TestCase from django.contrib.gis.geos import GEOSGeometry, Point, MultiPoint @@ -281,4 +282,11 @@ class RelatedGeoModelTest(TestCase): # evaluated as list generation swallows TypeError in CPython. sql = str(qs.query) + def test16_annotated_date_queryset(self): + "Ensure annotated date querysets work if spatial backend is used. See #14648." + birth_years = [dt.year for dt in + list(Author.objects.annotate(num_books=Count('books')).dates('dob', 'year'))] + birth_years.sort() + self.assertEqual([1950, 1974], birth_years) + # TODO: Related tests for KML, GML, and distance lookups.