From 806d898bbf7cc0f067674f4c112ab62786a3ce57 Mon Sep 17 00:00:00 2001 From: Justin Bronn Date: Tue, 7 Apr 2009 21:33:43 +0000 Subject: [PATCH] [1.0.X] Fixed #10757 -- Fixed improper selection of primary keys across relations when using `GeoManager.values`. Thanks, David Gouldin for ticket and initial patch. Backport of r10434 from trunk. git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@10437 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/gis/db/models/sql/query.py | 34 +++++++++++--------- django/contrib/gis/tests/relatedapp/tests.py | 20 ++++++++++++ 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/django/contrib/gis/db/models/sql/query.py b/django/contrib/gis/db/models/sql/query.py index 52f521d500..77a7796be0 100644 --- a/django/contrib/gis/db/models/sql/query.py +++ b/django/contrib/gis/db/models/sql/query.py @@ -66,7 +66,7 @@ class GeoQuery(sql.Query): # This loop customized for GeoQuery. for col, field in izip(self.select, self.select_fields): if isinstance(col, (list, tuple)): - r = self.get_field_select(field, col[0]) + r = self.get_field_select(field, col[0], col[1]) if with_aliases and col[1] in col_aliases: c_alias = 'Col%d' % len(col_aliases) result.append('%s AS %s' % (r, c_alias)) @@ -89,7 +89,7 @@ class GeoQuery(sql.Query): # This loop customized for GeoQuery. if not self.aggregate: for (table, col), field in izip(self.related_select_cols, self.related_select_fields): - r = self.get_field_select(field, table) + r = self.get_field_select(field, table, col) if with_aliases and col in col_aliases: c_alias = 'Col%d' % len(col_aliases) result.append('%s AS %s' % (r, c_alias)) @@ -219,19 +219,20 @@ class GeoQuery(sql.Query): sel_fmt = sel_fmt % self.custom_select[alias] return sel_fmt - def get_field_select(self, fld, alias=None): + def get_field_select(self, field, alias=None, column=None): """ Returns the SELECT SQL string for the given field. Figures out - if any custom selection SQL is needed for the column The `alias` - keyword may be used to manually specify the database table where - the column exists, if not in the model associated with this - `GeoQuery`. + if any custom selection SQL is needed for the column The `alias` + keyword may be used to manually specify the database table where + the column exists, if not in the model associated with this + `GeoQuery`. Similarly, `column` may be used to specify the exact + column name, rather than using the `column` attribute on `field`. """ - sel_fmt = self.get_select_format(fld) - if fld in self.custom_select: - field_sel = sel_fmt % self.custom_select[fld] + sel_fmt = self.get_select_format(field) + if field in self.custom_select: + field_sel = sel_fmt % self.custom_select[field] else: - field_sel = sel_fmt % self._field_column(fld, alias) + field_sel = sel_fmt % self._field_column(field, alias, column) return field_sel def get_select_format(self, fld): @@ -293,17 +294,18 @@ class GeoQuery(sql.Query): else: return False - def _field_column(self, field, table_alias=None): + def _field_column(self, field, table_alias=None, column=None): """ Helper function that returns the database column for the given field. The table and column are returned (quoted) in the proper format, e.g., - `"geoapp_city"."point"`. If `table_alias` is not specified, the + `"geoapp_city"."point"`. If `table_alias` is not specified, the database table associated with the model of this `GeoQuery` will be - used. + used. If `column` is specified, it will be used instead of the value + in `field.column`. """ if table_alias is None: table_alias = self.model._meta.db_table - return "%s.%s" % (self.quote_name_unless_alias(table_alias), - self.connection.ops.quote_name(field.column)) + return "%s.%s" % (self.quote_name_unless_alias(table_alias), + self.connection.ops.quote_name(column or field.column)) def _geo_field(self, field_name=None): """ diff --git a/django/contrib/gis/tests/relatedapp/tests.py b/django/contrib/gis/tests/relatedapp/tests.py index 47d49c73e9..827ea1d989 100644 --- a/django/contrib/gis/tests/relatedapp/tests.py +++ b/django/contrib/gis/tests/relatedapp/tests.py @@ -95,6 +95,26 @@ class RelatedGeoModelTest(unittest.TestCase): # Regression test for #9752. l = list(DirectoryEntry.objects.all().select_related()) + def test09_pk_relations(self): + "Ensuring correct primary key column is selected across relations. See #10757." + # Adding two more cities, but this time making sure that their location + # ID values do not match their City ID values. + loc1 = Location.objects.create(point='POINT (-95.363151 29.763374)') + loc2 = Location.objects.create(point='POINT (-96.801611 32.782057)') + dallas = City.objects.create(name='Dallas', location=loc2) + houston = City.objects.create(name='Houston', location=loc1) + + # The expected ID values -- notice the last two location IDs + # are out of order. We want to make sure that the related + # location ID column is selected instead of ID column for + # the city. + city_ids = (1, 2, 3, 4, 5) + loc_ids = (1, 2, 3, 5, 4) + ids_qs = City.objects.order_by('id').values('id', 'location__id') + for val_dict, c_id, l_id in zip(ids_qs, city_ids, loc_ids): + self.assertEqual(val_dict['id'], c_id) + self.assertEqual(val_dict['location__id'], l_id) + # TODO: Related tests for KML, GML, and distance lookups. def suite():