Fixed #10757 -- Fixed improper selection of primary keys across relations when using `GeoManager.values`.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10434 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2009-04-07 21:18:41 +00:00
parent 710dfea913
commit a6356cad4d
2 changed files with 35 additions and 13 deletions

View File

@ -74,7 +74,7 @@ class GeoQuery(sql.Query):
table = self.alias_map[alias][TABLE_NAME] table = self.alias_map[alias][TABLE_NAME]
if table in only_load and col not in only_load[table]: if table in only_load and col not in only_load[table]:
continue continue
r = self.get_field_select(field, alias) r = self.get_field_select(field, alias, column)
if with_aliases: if with_aliases:
if col[1] in col_aliases: if col[1] in col_aliases:
c_alias = 'Col%d' % len(col_aliases) c_alias = 'Col%d' % len(col_aliases)
@ -112,7 +112,7 @@ class GeoQuery(sql.Query):
# This loop customized for GeoQuery. # This loop customized for GeoQuery.
for (table, col), field in izip(self.related_select_cols, self.related_select_fields): 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: if with_aliases and col in col_aliases:
c_alias = 'Col%d' % len(col_aliases) c_alias = 'Col%d' % len(col_aliases)
result.append('%s AS %s' % (r, c_alias)) result.append('%s AS %s' % (r, c_alias))
@ -213,7 +213,8 @@ class GeoQuery(sql.Query):
values = [self.convert_values(v, self.extra_select_fields.get(a, None)) values = [self.convert_values(v, self.extra_select_fields.get(a, None))
for v, a in izip(row[rn_offset:index_start], aliases)] for v, a in izip(row[rn_offset:index_start], aliases)]
if SpatialBackend.oracle or getattr(self, 'geo_values', False): 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): for value, field in izip(row[index_start:], fields):
values.append(self.convert_values(value, field)) values.append(self.convert_values(value, field))
else: else:
@ -260,19 +261,20 @@ class GeoQuery(sql.Query):
sel_fmt = sel_fmt % self.custom_select[alias] sel_fmt = sel_fmt % self.custom_select[alias]
return sel_fmt 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 Returns the SELECT SQL string for the given field. Figures out
if any custom selection SQL is needed for the column The `alias` if any custom selection SQL is needed for the column The `alias`
keyword may be used to manually specify the database table where keyword may be used to manually specify the database table where
the column exists, if not in the model associated with this 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) sel_fmt = self.get_select_format(field)
if fld in self.custom_select: if field in self.custom_select:
field_sel = sel_fmt % self.custom_select[fld] field_sel = sel_fmt % self.custom_select[field]
else: else:
field_sel = sel_fmt % self._field_column(fld, alias) field_sel = sel_fmt % self._field_column(field, alias, column)
return field_sel return field_sel
def get_select_format(self, fld): def get_select_format(self, fld):
@ -302,17 +304,18 @@ class GeoQuery(sql.Query):
return sel_fmt return sel_fmt
# Private API utilities, subject to change. # 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. Helper function that returns the database column for the given field.
The table and column are returned (quoted) in the proper format, e.g., 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 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 if table_alias is None: table_alias = self.model._meta.db_table
return "%s.%s" % (self.quote_name_unless_alias(table_alias), 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): def _geo_field(self, field_name=None):
""" """

View File

@ -183,7 +183,6 @@ class RelatedGeoModelTest(unittest.TestCase):
self.assertEqual(m.point, d['point']) self.assertEqual(m.point, d['point'])
self.assertEqual(m.point, t[1]) self.assertEqual(m.point, t[1])
# Test disabled until #10572 is resolved.
def test08_defer_only(self): def test08_defer_only(self):
"Testing defer() and only() on Geographic models." "Testing defer() and only() on Geographic models."
qs = Location.objects.all() qs = Location.objects.all()
@ -191,6 +190,26 @@ class RelatedGeoModelTest(unittest.TestCase):
for loc, def_loc in zip(qs, def_qs): for loc, def_loc in zip(qs, def_qs):
self.assertEqual(loc.point, def_loc.point) 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. # TODO: Related tests for KML, GML, and distance lookups.
def suite(): def suite():