From f7cf58ac0ebf720e5c48f00e99c31cf39c4fca50 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 1 Apr 2010 16:48:16 +0000 Subject: 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 + .../raw_query/fixtures/initial_data.json | 102 ------------------- .../raw_query/fixtures/raw_query_books.json | 110 +++++++++++++++++++++ tests/modeltests/raw_query/models.py | 2 + tests/modeltests/raw_query/tests.py | 15 ++- 7 files changed, 137 insertions(+), 112 deletions(-) delete mode 100644 tests/modeltests/raw_query/fixtures/initial_data.json create mode 100644 tests/modeltests/raw_query/fixtures/raw_query_books.json 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/initial_data.json deleted file mode 100644 index 3ff9810b45..0000000000 --- a/tests/modeltests/raw_query/fixtures/initial_data.json +++ /dev/null @@ -1,102 +0,0 @@ -[ - { - "pk": 1, - "model": "raw_query.author", - "fields": { - "dob": "1950-09-20", - "first_name": "Joe", - "last_name": "Smith" - } - }, - { - "pk": 2, - "model": "raw_query.author", - "fields": { - "dob": "1920-04-02", - "first_name": "Jill", - "last_name": "Doe" - } - }, - { - "pk": 3, - "model": "raw_query.author", - "fields": { - "dob": "1986-01-25", - "first_name": "Bob", - "last_name": "Smith" - } - }, - { - "pk": 4, - "model": "raw_query.author", - "fields": { - "dob": "1932-05-10", - "first_name": "Bill", - "last_name": "Jones" - } - }, - { - "pk": 1, - "model": "raw_query.book", - "fields": { - "author": 1, - "title": "The awesome book" - } - }, - { - "pk": 2, - "model": "raw_query.book", - "fields": { - "author": 1, - "title": "The horrible book" - } - }, - { - "pk": 3, - "model": "raw_query.book", - "fields": { - "author": 1, - "title": "Another awesome book" - } - }, - { - "pk": 4, - "model": "raw_query.book", - "fields": { - "author": 3, - "title": "Some other book" - } - }, - { - "pk": 1, - "model": "raw_query.coffee", - "fields": { - "brand": "dunkin doughnuts" - } - }, - { - "pk": 2, - "model": "raw_query.coffee", - "fields": { - "brand": "starbucks" - } - }, - { - "pk": 1, - "model": "raw_query.reviewer", - "fields": { - "reviewed": [ - 2, - 3, - 4 - ] - } - }, - { - "pk": 2, - "model": "raw_query.reviewer", - "fields": { - "reviewed": [] - } - } -] diff --git a/tests/modeltests/raw_query/fixtures/raw_query_books.json b/tests/modeltests/raw_query/fixtures/raw_query_books.json new file mode 100644 index 0000000000..35879aa5eb --- /dev/null +++ b/tests/modeltests/raw_query/fixtures/raw_query_books.json @@ -0,0 +1,110 @@ +[ + { + "pk": 1, + "model": "raw_query.author", + "fields": { + "dob": "1950-09-20", + "first_name": "Joe", + "last_name": "Smith" + } + }, + { + "pk": 2, + "model": "raw_query.author", + "fields": { + "dob": "1920-04-02", + "first_name": "Jill", + "last_name": "Doe" + } + }, + { + "pk": 3, + "model": "raw_query.author", + "fields": { + "dob": "1986-01-25", + "first_name": "Bob", + "last_name": "Smith" + } + }, + { + "pk": 4, + "model": "raw_query.author", + "fields": { + "dob": "1932-05-10", + "first_name": "Bill", + "last_name": "Jones" + } + }, + { + "pk": 1, + "model": "raw_query.book", + "fields": { + "author": 1, + "title": "The awesome book", + "paperback": false, + "opening_line": "It was a bright cold day in April and the clocks were striking thirteen." + } + }, + { + "pk": 2, + "model": "raw_query.book", + "fields": { + "author": 1, + "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." + } + }, + { + "pk": 3, + "model": "raw_query.book", + "fields": { + "author": 1, + "title": "Another awesome book", + "paperback": false, + "opening_line": "A squat grey building of only thirty-four stories." + } + }, + { + "pk": 4, + "model": "raw_query.book", + "fields": { + "author": 3, + "title": "Some other book", + "paperback": true, + "opening_line": "It was the day my grandmother exploded." + } + }, + { + "pk": 1, + "model": "raw_query.coffee", + "fields": { + "brand": "dunkin doughnuts" + } + }, + { + "pk": 2, + "model": "raw_query.coffee", + "fields": { + "brand": "starbucks" + } + }, + { + "pk": 1, + "model": "raw_query.reviewer", + "fields": { + "reviewed": [ + 2, + 3, + 4 + ] + } + }, + { + "pk": 2, + "model": "raw_query.reviewer", + "fields": { + "reviewed": [] + } + } +] 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) -- cgit v1.3