From f7cf58ac0ebf720e5c48f00e99c31cf39c4fca50 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 1 Apr 2010 16:48:16 +0000 Subject: [PATCH] Fixed #12429 -- Ensure that raw queries call resolve_columns if the backend defines it. This ensures (as much as possible) that the model values returned by a raw query match that in normal queries. Thanks to Ian Kelly for the report. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12904 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/db/models/query.py | 11 +++++++++-- django/db/models/sql/compiler.py | 4 ---- django/db/models/sql/query.py | 5 +++++ .../{initial_data.json => raw_query_books.json} | 16 ++++++++++++---- tests/modeltests/raw_query/models.py | 2 ++ tests/modeltests/raw_query/tests.py | 15 +++++++++++---- 6 files changed, 39 insertions(+), 14 deletions(-) rename tests/modeltests/raw_query/fixtures/{initial_data.json => raw_query_books.json} (71%) diff --git a/django/db/models/query.py b/django/db/models/query.py index d0b605db84..3567671a1a 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1407,20 +1407,27 @@ class RawQuerySet(object): self._model_fields = {} for field in self.model._meta.fields: name, column = field.get_attname_column() - self._model_fields[converter(column)] = name + self._model_fields[converter(column)] = field return self._model_fields def transform_results(self, values): model_init_kwargs = {} annotations = () + # Perform database backend type resolution + connection = connections[self.db] + compiler = connection.ops.compiler('SQLCompiler')(self.query, connection, self.db) + if hasattr(compiler, 'resolve_columns'): + fields = [self.model_fields.get(c,None) for c in self.columns] + values = compiler.resolve_columns(values, fields) + # Associate fields to values for pos, value in enumerate(values): column = self.columns[pos] # Separate properties from annotations if column in self.model_fields.keys(): - model_init_kwargs[self.model_fields[column]] = value + model_init_kwargs[self.model_fields[column].attname] = value else: annotations += (column, value), diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index a07e506fc7..84c28a0d35 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -14,10 +14,6 @@ class SQLCompiler(object): self.using = using self.quote_cache = {} - # Check that the compiler will be able to execute the query - for alias, aggregate in self.query.aggregate_select.items(): - self.connection.ops.check_aggregate_support(aggregate) - def pre_sql_setup(self): """ Does any necessary class setup immediately prior to producing SQL. This diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 9819be09a1..72d4bc9c1b 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -189,6 +189,11 @@ class Query(object): raise ValueError("Need either using or connection") if using: connection = connections[using] + + # Check that the compiler will be able to execute the query + for alias, aggregate in self.aggregate_select.items(): + connection.ops.check_aggregate_support(aggregate) + return connection.ops.compiler(self.compiler)(self, connection, using) def get_meta(self): diff --git a/tests/modeltests/raw_query/fixtures/initial_data.json b/tests/modeltests/raw_query/fixtures/raw_query_books.json similarity index 71% rename from tests/modeltests/raw_query/fixtures/initial_data.json rename to tests/modeltests/raw_query/fixtures/raw_query_books.json index 3ff9810b45..35879aa5eb 100644 --- a/tests/modeltests/raw_query/fixtures/initial_data.json +++ b/tests/modeltests/raw_query/fixtures/raw_query_books.json @@ -40,7 +40,9 @@ "model": "raw_query.book", "fields": { "author": 1, - "title": "The awesome book" + "title": "The awesome book", + "paperback": false, + "opening_line": "It was a bright cold day in April and the clocks were striking thirteen." } }, { @@ -48,7 +50,9 @@ "model": "raw_query.book", "fields": { "author": 1, - "title": "The horrible book" + "title": "The horrible book", + "paperback": true, + "opening_line": "On an evening in the latter part of May a middle-aged man was walking homeward from Shaston to the village of Marlott, in the adjoining Vale of Blakemore, or Blackmoor." } }, { @@ -56,7 +60,9 @@ "model": "raw_query.book", "fields": { "author": 1, - "title": "Another awesome book" + "title": "Another awesome book", + "paperback": false, + "opening_line": "A squat grey building of only thirty-four stories." } }, { @@ -64,7 +70,9 @@ "model": "raw_query.book", "fields": { "author": 3, - "title": "Some other book" + "title": "Some other book", + "paperback": true, + "opening_line": "It was the day my grandmother exploded." } }, { diff --git a/tests/modeltests/raw_query/models.py b/tests/modeltests/raw_query/models.py index 6188d58cb3..bb42b5be2b 100644 --- a/tests/modeltests/raw_query/models.py +++ b/tests/modeltests/raw_query/models.py @@ -17,6 +17,8 @@ class Author(models.Model): class Book(models.Model): title = models.CharField(max_length=255) author = models.ForeignKey(Author) + paperback = models.BooleanField() + opening_line = models.TextField() class Coffee(models.Model): brand = models.CharField(max_length=255, db_column="name") diff --git a/tests/modeltests/raw_query/tests.py b/tests/modeltests/raw_query/tests.py index 447849abb9..a0325eff97 100644 --- a/tests/modeltests/raw_query/tests.py +++ b/tests/modeltests/raw_query/tests.py @@ -7,6 +7,7 @@ from models import Author, Book, Coffee, Reviewer, FriendlyAuthor class RawQueryTests(TestCase): + fixtures = ['raw_query_books.json'] def assertSuccessfulRawQuery(self, model, query, expected_results, expected_annotations=(), params=[], translations=None): @@ -14,10 +15,10 @@ class RawQueryTests(TestCase): Execute the passed query against the passed model and check the output """ results = list(model.objects.raw(query, params=params, translations=translations)) - self.assertProcessed(results, expected_results, expected_annotations) + self.assertProcessed(model, results, expected_results, expected_annotations) self.assertAnnotations(results, expected_annotations) - def assertProcessed(self, results, orig, expected_annotations=()): + def assertProcessed(self, model, results, orig, expected_annotations=()): """ Compare the results of a raw query against expected results """ @@ -27,7 +28,13 @@ class RawQueryTests(TestCase): for annotation in expected_annotations: setattr(orig_item, *annotation) - self.assertEqual(item.id, orig_item.id) + for field in model._meta.fields: + # Check that all values on the model are equal + self.assertEquals(getattr(item,field.attname), + getattr(orig_item,field.attname)) + # This includes checking that they are the same type + self.assertEquals(type(getattr(item,field.attname)), + type(getattr(orig_item,field.attname))) def assertNoAnnotations(self, results): """ @@ -115,7 +122,7 @@ class RawQueryTests(TestCase): author = Author.objects.all()[2] params = [author.first_name] results = list(Author.objects.raw(query, params=params)) - self.assertProcessed(results, [author]) + self.assertProcessed(Author, results, [author]) self.assertNoAnnotations(results) self.assertEqual(len(results), 1)