From 9f6859e1eac0c39e2e4fb79a1336eb63b0295fec Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Sun, 26 Feb 2012 14:35:46 +0000 Subject: [PATCH] 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 --- django/contrib/gis/db/backends/mysql/operations.py | 2 +- django/contrib/gis/db/models/sql/compiler.py | 7 ++++--- django/contrib/gis/db/models/sql/query.py | 4 ++-- django/contrib/gis/tests/geoapp/models.py | 4 ++++ django/contrib/gis/tests/geoapp/test_regress.py | 10 +++++++++- django/db/backends/mysql/base.py | 6 ++++++ django/db/backends/mysql/compiler.py | 5 +---- 7 files changed, 27 insertions(+), 11 deletions(-) diff --git a/django/contrib/gis/db/backends/mysql/operations.py b/django/contrib/gis/db/backends/mysql/operations.py index 1653636dd4..c0e5aa6691 100644 --- a/django/contrib/gis/db/backends/mysql/operations.py +++ b/django/contrib/gis/db/backends/mysql/operations.py @@ -5,7 +5,7 @@ from django.contrib.gis.db.backends.base import 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 name = 'mysql' select = 'AsText(%s)' diff --git a/django/contrib/gis/db/models/sql/compiler.py b/django/contrib/gis/db/models/sql/compiler.py index 07eea32b69..93590256b4 100644 --- a/django/contrib/gis/db/models/sql/compiler.py +++ b/django/contrib/gis/db/models/sql/compiler.py @@ -185,9 +185,10 @@ class GeoSQLCompiler(compiler.SQLCompiler): self.query.extra_select_fields.get(a, None), self.connection) 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 - # the `geo_values` attribute is defined. + + if self.connection.ops.oracle or self.connection.ops.mysql or getattr(self.query, 'geo_values', False): + # 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): values.append(self.query.convert_values(value, field, self.connection)) else: diff --git a/django/contrib/gis/db/models/sql/query.py b/django/contrib/gis/db/models/sql/query.py index c300dcd527..25c0376996 100644 --- a/django/contrib/gis/db/models/sql/query.py +++ b/django/contrib/gis/db/models/sql/query.py @@ -56,8 +56,8 @@ class GeoQuery(sql.Query): extra selection objects into Geometry and Distance objects. TODO: Make converted objects 'lazy' for less overhead. """ - if connection.ops.oracle: - # Running through Oracle's first. + if connection.ops.oracle or connection.ops.mysql: + # On MySQL and Oracle, call their version of `convert_values` first. value = super(GeoQuery, self).convert_values(value, field or GeomField(), connection) if value is None: diff --git a/django/contrib/gis/tests/geoapp/models.py b/django/contrib/gis/tests/geoapp/models.py index 1ac830953a..79061e1cfc 100644 --- a/django/contrib/gis/tests/geoapp/models.py +++ b/django/contrib/gis/tests/geoapp/models.py @@ -34,6 +34,10 @@ class Track(models.Model): objects = models.GeoManager() def __unicode__(self): return self.name +class Truth(models.Model): + val = models.BooleanField() + objects = models.GeoManager() + if not spatialite: class Feature(models.Model): name = models.CharField(max_length=20) diff --git a/django/contrib/gis/tests/geoapp/test_regress.py b/django/contrib/gis/tests/geoapp/test_regress.py index 1e68440286..c9a2d83625 100644 --- a/django/contrib/gis/tests/geoapp/test_regress.py +++ b/django/contrib/gis/tests/geoapp/test_regress.py @@ -7,7 +7,7 @@ from django.contrib.gis.shortcuts import render_to_kmz from django.db.models import Count from django.test import TestCase -from .models import City, PennsylvaniaCity, State +from .models import City, PennsylvaniaCity, State, Truth class GeoRegressionTests(TestCase): @@ -64,3 +64,11 @@ class GeoRegressionTests(TestCase): "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')).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) diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 830808b9d3..8073e55cf4 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -176,6 +176,12 @@ class DatabaseFeatures(BaseDatabaseFeatures): class DatabaseOperations(BaseDatabaseOperations): 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): # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html if lookup_type == 'week_day': diff --git a/django/db/backends/mysql/compiler.py b/django/db/backends/mysql/compiler.py index bd4105ff8e..3fd536c0c6 100644 --- a/django/db/backends/mysql/compiler.py +++ b/django/db/backends/mysql/compiler.py @@ -5,10 +5,7 @@ class SQLCompiler(compiler.SQLCompiler): values = [] index_extra_select = len(self.query.extra_select.keys()) for value, field in map(None, row[index_extra_select:], fields): - if (field and field.get_internal_type() in ("BooleanField", "NullBooleanField") and - value in (0, 1)): - value = bool(value) - values.append(value) + values.append(self.query.convert_values(value, field, connection=self.connection)) return row[:index_extra_select] + tuple(values) class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler):