Skip to content

Commit

Permalink
Fixed #10757 -- Fixed improper selection of primary keys across relat…
Browse files Browse the repository at this point in the history
…ions when using `GeoManager.values`.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10434 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
jbronn committed Apr 7, 2009
1 parent 710dfea commit a6356ca
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 13 deletions.
27 changes: 15 additions & 12 deletions django/contrib/gis/db/models/sql/query.py
Expand Up @@ -74,7 +74,7 @@ def get_columns(self, with_aliases=False):
table = self.alias_map[alias][TABLE_NAME]
if table in only_load and col not in only_load[table]:
continue
r = self.get_field_select(field, alias)
r = self.get_field_select(field, alias, column)
if with_aliases:
if col[1] in col_aliases:
c_alias = 'Col%d' % len(col_aliases)
Expand Down Expand Up @@ -112,7 +112,7 @@ def get_columns(self, with_aliases=False):

# This loop customized for GeoQuery.
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))
Expand Down Expand Up @@ -213,7 +213,8 @@ def resolve_columns(self, row, fields=()):
values = [self.convert_values(v, self.extra_select_fields.get(a, None))
for v, a in izip(row[rn_offset:index_start], aliases)]
if SpatialBackend.oracle or getattr(self, 'geo_values', False):
# We resolve the columns
# We resolve the rest of the columns if we're on Oracle or if
# the `geo_values` attribute is defined.
for value, field in izip(row[index_start:], fields):
values.append(self.convert_values(value, field))
else:
Expand Down Expand Up @@ -260,19 +261,20 @@ def get_extra_select_format(self, alias):
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`.
`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):
Expand Down Expand Up @@ -302,17 +304,18 @@ def get_select_format(self, fld):
return sel_fmt

# Private API utilities, subject to change.
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
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))
self.connection.ops.quote_name(column or field.column))

def _geo_field(self, field_name=None):
"""
Expand Down
21 changes: 20 additions & 1 deletion django/contrib/gis/tests/relatedapp/tests.py
Expand Up @@ -183,14 +183,33 @@ def test07_values(self):
self.assertEqual(m.point, d['point'])
self.assertEqual(m.point, t[1])

# Test disabled until #10572 is resolved.
def test08_defer_only(self):
"Testing defer() and only() on Geographic models."
qs = Location.objects.all()
def_qs = Location.objects.defer('point')
for loc, def_loc in zip(qs, def_qs):
self.assertEqual(loc.point, def_loc.point)

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():
Expand Down

0 comments on commit a6356ca

Please sign in to comment.