From 59ac04a54dde3a489f7948b6e6d6170d74aa80c8 Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Fri, 14 Mar 2008 11:46:26 +0000 Subject: [PATCH] queryset-refactor: Second part of select_related() fix. Relations on the parent model can now be specified as part of the fields list. Fixed #6761. git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7241 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/db/models/sql/query.py | 20 +++++++++--- tests/modeltests/model_inheritance/models.py | 32 +++++++++++++++++--- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 77f3519516..6d36ac3e40 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -769,10 +769,10 @@ class Query(object): return if not opts: opts = self.get_meta() - root_alias = self.tables[0] + root_alias = self.get_initial_alias() self.select.extend(self.get_default_columns()) if not used: - used = [] + used = set() # Setup for the case when only particular related fields should be # included in the related selection. @@ -783,14 +783,24 @@ class Query(object): else: restricted = False - for f in opts.fields: + for f, model in opts.get_fields_with_model(): if (not f.rel or (restricted and f.name not in requested) or (not restricted and f.null) or f.rel.parent_link): continue table = f.rel.to._meta.db_table - alias = self.join((root_alias, table, f.column, + if model: + int_opts = opts + alias = root_alias + for int_model in opts.get_base_chain(model): + lhs_col = int_opts.parents[int_model].column + int_opts = int_model._meta + alias = self.join((alias, int_opts.db_table, lhs_col, + int_opts.pk.column), exclusions=used) + else: + alias = root_alias + alias = self.join((alias, table, f.column, f.rel.get_related_field().column), exclusions=used) - used.append(alias) + used.add(alias) self.select.extend([(alias, f2.column) for f2 in f.rel.to._meta.fields]) if restricted: diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py index 3f13dabc72..4a8c00fb52 100644 --- a/tests/modeltests/model_inheritance/models.py +++ b/tests/modeltests/model_inheritance/models.py @@ -42,6 +42,12 @@ class Student(CommonInfo): # Multi-table inheritance # +class Chef(models.Model): + name = models.CharField(max_length=50) + + def __unicode__(self): + return u"%s the chef" % self.name + class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) @@ -59,6 +65,7 @@ class Rating(models.Model): class Restaurant(Place, Rating): serves_hot_dogs = models.BooleanField() serves_pizza = models.BooleanField() + chef = models.ForeignKey(Chef, null=True, blank=True) class Meta(Rating.Meta): db_table = 'my_restaurant' @@ -136,7 +143,9 @@ Test constructor for Restaurant. >>> r.save() # Test the constructor for ItalianRestaurant. ->>> ir = ItalianRestaurant(name='Ristorante Miron', address='1234 W. Ash', serves_hot_dogs=False, serves_pizza=False, serves_gnocchi=True, rating=4) +>>> c = Chef(name="Albert") +>>> c.save() +>>> ir = ItalianRestaurant(name='Ristorante Miron', address='1234 W. Ash', serves_hot_dogs=False, serves_pizza=False, serves_gnocchi=True, rating=4, chef=c) >>> ir.save() >>> ir.address = '1234 W. Elm' >>> ir.save() @@ -144,9 +153,9 @@ Test constructor for Restaurant. # Make sure Restaurant and ItalianRestaurant have the right fields in the right # order. >>> [f.name for f in Restaurant._meta.fields] -['id', 'name', 'address', 'place_ptr', 'rating', 'serves_hot_dogs', 'serves_pizza'] +['id', 'name', 'address', 'place_ptr', 'rating', 'serves_hot_dogs', 'serves_pizza', 'chef'] >>> [f.name for f in ItalianRestaurant._meta.fields] -['id', 'name', 'address', 'place_ptr', 'rating', 'serves_hot_dogs', 'serves_pizza', 'restaurant_ptr', 'serves_gnocchi'] +['id', 'name', 'address', 'place_ptr', 'rating', 'serves_hot_dogs', 'serves_pizza', 'chef', 'restaurant_ptr', 'serves_gnocchi'] >>> Restaurant._meta.ordering ['-rating'] @@ -158,7 +167,7 @@ Test constructor for Restaurant. >>> Restaurant.objects.filter(supplier__name='foo') Traceback (most recent call last): ... -FieldError: Cannot resolve keyword 'supplier' into field. Choices are: address, id, italianrestaurant, lot, name, place_ptr, provider, rating, serves_hot_dogs, serves_pizza +FieldError: Cannot resolve keyword 'supplier' into field. Choices are: address, chef, id, italianrestaurant, lot, name, place_ptr, provider, rating, serves_hot_dogs, serves_pizza # Parent fields can be used directly in filters on the child model. >>> Restaurant.objects.filter(name='Demon Dogs') @@ -240,4 +249,19 @@ u'Demon Puppies' >>> list(ItalianRestaurant.objects.values('name', 'rating')) == [d] True +# select_related works with fields from the parent object as if they were a +# normal part of the model. +>>> from django import db +>>> from django.conf import settings +>>> settings.DEBUG = True +>>> db.reset_queries() +>>> ItalianRestaurant.objects.all()[0].chef + +>>> len(db.connection.queries) +2 +>>> ItalianRestaurant.objects.select_related('chef')[0].chef + +>>> len(db.connection.queries) +3 + """}