Fixed #15169 -- Corrected handling of model boolean fields in MySQL spatial backend.

Thanks goes to zmsmith and others for reporting the issue and to Justin Bronn for the fix.

Refs #7190, r12578, r12900, #13293, r12939.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17588 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Ramiro Morales 2012-02-26 14:35:46 +00:00
parent 5a013cebd9
commit 9f6859e1ea
7 changed files with 27 additions and 11 deletions

View File

@ -5,7 +5,7 @@ from django.contrib.gis.db.backends.base import BaseSpatialOperations
class MySQLOperations(DatabaseOperations, BaseSpatialOperations): class MySQLOperations(DatabaseOperations, BaseSpatialOperations):
compiler_module = 'django.contrib.gis.db.models.sql.compiler' compiler_module = 'django.contrib.gis.db.backends.mysql.compiler'
mysql = True mysql = True
name = 'mysql' name = 'mysql'
select = 'AsText(%s)' select = 'AsText(%s)'

View File

@ -185,9 +185,10 @@ class GeoSQLCompiler(compiler.SQLCompiler):
self.query.extra_select_fields.get(a, None), self.query.extra_select_fields.get(a, None),
self.connection) self.connection)
for v, a in izip(row[rn_offset:index_start], aliases)] for v, a in izip(row[rn_offset:index_start], aliases)]
if self.connection.ops.oracle or getattr(self.query, 'geo_values', False):
# We resolve the rest of the columns if we're on Oracle or if if self.connection.ops.oracle or self.connection.ops.mysql or getattr(self.query, 'geo_values', False):
# the `geo_values` attribute is defined. # We resolve the rest of the columns if we're on MySQL, Oracle or
# if 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, self.connection)) values.append(self.query.convert_values(value, field, self.connection))
else: else:

View File

@ -56,8 +56,8 @@ class GeoQuery(sql.Query):
extra selection objects into Geometry and Distance objects. extra selection objects into Geometry and Distance objects.
TODO: Make converted objects 'lazy' for less overhead. TODO: Make converted objects 'lazy' for less overhead.
""" """
if connection.ops.oracle: if connection.ops.oracle or connection.ops.mysql:
# Running through Oracle's first. # On MySQL and Oracle, call their version of `convert_values` first.
value = super(GeoQuery, self).convert_values(value, field or GeomField(), connection) value = super(GeoQuery, self).convert_values(value, field or GeomField(), connection)
if value is None: if value is None:

View File

@ -34,6 +34,10 @@ class Track(models.Model):
objects = models.GeoManager() objects = models.GeoManager()
def __unicode__(self): return self.name def __unicode__(self): return self.name
class Truth(models.Model):
val = models.BooleanField()
objects = models.GeoManager()
if not spatialite: if not spatialite:
class Feature(models.Model): class Feature(models.Model):
name = models.CharField(max_length=20) name = models.CharField(max_length=20)

View File

@ -7,7 +7,7 @@ from django.contrib.gis.shortcuts import render_to_kmz
from django.db.models import Count from django.db.models import Count
from django.test import TestCase from django.test import TestCase
from .models import City, PennsylvaniaCity, State from .models import City, PennsylvaniaCity, State, Truth
class GeoRegressionTests(TestCase): class GeoRegressionTests(TestCase):
@ -64,3 +64,11 @@ class GeoRegressionTests(TestCase):
"Regression for #16409 - make sure defer() and only() work with annotate()" "Regression for #16409 - make sure defer() and only() work with annotate()"
self.assertIsInstance(list(City.objects.annotate(Count('point')).defer('name')), list) self.assertIsInstance(list(City.objects.annotate(Count('point')).defer('name')), list)
self.assertIsInstance(list(City.objects.annotate(Count('point')).only('name')), list) self.assertIsInstance(list(City.objects.annotate(Count('point')).only('name')), list)
def test04_boolean_conversion(self):
"Testing Boolean value conversion with the spatial backend, see #15169."
t1 = Truth.objects.create(val=True)
t2 = Truth.objects.create(val=False)
self.assertTrue(Truth.objects.get(pk=1).val is True)
self.assertTrue(Truth.objects.get(pk=2).val is False)

View File

@ -176,6 +176,12 @@ class DatabaseFeatures(BaseDatabaseFeatures):
class DatabaseOperations(BaseDatabaseOperations): class DatabaseOperations(BaseDatabaseOperations):
compiler_module = "django.db.backends.mysql.compiler" compiler_module = "django.db.backends.mysql.compiler"
def convert_values(self, value, field):
if (field and field.get_internal_type() in ("BooleanField", "NullBooleanField") and
value in (0, 1)):
value = bool(value)
return value
def date_extract_sql(self, lookup_type, field_name): def date_extract_sql(self, lookup_type, field_name):
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
if lookup_type == 'week_day': if lookup_type == 'week_day':

View File

@ -5,10 +5,7 @@ class SQLCompiler(compiler.SQLCompiler):
values = [] values = []
index_extra_select = len(self.query.extra_select.keys()) index_extra_select = len(self.query.extra_select.keys())
for value, field in map(None, row[index_extra_select:], fields): for value, field in map(None, row[index_extra_select:], fields):
if (field and field.get_internal_type() in ("BooleanField", "NullBooleanField") and values.append(self.query.convert_values(value, field, connection=self.connection))
value in (0, 1)):
value = bool(value)
values.append(value)
return row[:index_extra_select] + tuple(values) return row[:index_extra_select] + tuple(values)
class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler): class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler):