mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	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
This commit is contained in:
		| @@ -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 |  | ||||||
| @@ -48,7 +48,7 @@ def get_dist_ops(operator): | |||||||
|     return (SpatiaLiteDistance(operator),) |     return (SpatiaLiteDistance(operator),) | ||||||
|  |  | ||||||
| class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations): | class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations): | ||||||
|     compiler_module = 'django.contrib.gis.db.backends.spatialite.compiler' |     compiler_module = 'django.contrib.gis.db.models.sql.compiler' | ||||||
|     name = 'spatialite' |     name = 'spatialite' | ||||||
|     spatialite = True |     spatialite = True | ||||||
|     version_regex = re.compile(r'^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)') |     version_regex = re.compile(r'^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)') | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| from itertools import izip | 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 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 | from django.db.models.sql.query import get_proxied_model | ||||||
|  |  | ||||||
| SQLCompiler = compiler.SQLCompiler | 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 |             # We resolve the rest of the columns if we're on Oracle or if | ||||||
|             # the `geo_values` attribute is defined. |             # the `geo_values` attribute is defined. | ||||||
|             for value, field in map(None, row[index_start:], fields): |             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: |         else: | ||||||
|             values.extend(row[index_start:]) |             values.extend(row[index_start:]) | ||||||
|         return tuple(values) |         return tuple(values) | ||||||
| @@ -275,4 +275,24 @@ class SQLAggregateCompiler(compiler.SQLAggregateCompiler, GeoSQLCompiler): | |||||||
|     pass |     pass | ||||||
|  |  | ||||||
| class SQLDateCompiler(compiler.SQLDateCompiler, GeoSQLCompiler): | 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 | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							| @@ -36,6 +36,7 @@ class Parcel(models.Model): | |||||||
| # These use the GeoManager but do not have any geographic fields. | # These use the GeoManager but do not have any geographic fields. | ||||||
| class Author(models.Model): | class Author(models.Model): | ||||||
|     name = models.CharField(max_length=100) |     name = models.CharField(max_length=100) | ||||||
|  |     dob = models.DateField() | ||||||
|     objects = models.GeoManager() |     objects = models.GeoManager() | ||||||
|  |  | ||||||
| class Article(models.Model): | class Article(models.Model): | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | from datetime import date | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
|  |  | ||||||
| from django.contrib.gis.geos import GEOSGeometry, Point, MultiPoint | from django.contrib.gis.geos import GEOSGeometry, Point, MultiPoint | ||||||
| @@ -281,4 +282,11 @@ class RelatedGeoModelTest(TestCase): | |||||||
|         # evaluated as list generation swallows TypeError in CPython. |         # evaluated as list generation swallows TypeError in CPython. | ||||||
|         sql = str(qs.query) |         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. |     # TODO: Related tests for KML, GML, and distance lookups. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user