Ticket #14694: 14694-defer-reverse-relations-r17008.diff

File 14694-defer-reverse-relations-r17008.diff, 5.6 KB (added by mrmachine, 4 years ago)

fix and tests

  • django/db/models/sql/query.py

     
    1616from django.db.models import signals
    1717from django.db.models.fields import FieldDoesNotExist
    1818from django.db.models.query_utils import InvalidQuery
     19from django.db.models.related import RelatedObject
    1920from django.db.models.sql import aggregates as base_aggregates_module
    2021from django.db.models.sql.constants import *
    2122from django.db.models.sql.datastructures import EmptyResultSet, Empty, MultiJoin
     
    580581            for name in parts[:-1]:
    581582                old_model = cur_model
    582583                source = opts.get_field_by_name(name)[0]
    583                 cur_model = opts.get_field_by_name(name)[0].rel.to
     584                if isinstance(source, RelatedObject):
     585                    cur_model = source.model
     586                else:
     587                    cur_model = source.rel.to
    584588                opts = cur_model._meta
    585589                # Even if we're "just passing through" this model, we must add
    586590                # both the current model's pk and the related reference field
    587                 # to the things we select.
    588                 must_include[old_model].add(source)
     591                # (if it's not a reverse relation) to the things we select.
     592                if not isinstance(source, RelatedObject):
     593                    must_include[old_model].add(source)
    589594                add_to_dict(must_include, cur_model, opts.pk)
    590595            field, model, _, _ = opts.get_field_by_name(parts[-1])
    591596            if model is None:
     
    630635            for model, values in seen.iteritems():
    631636                callback(target, model, values)
    632637
    633 
    634638    def deferred_to_columns_cb(self, target, model, fields):
    635639        """
    636640        Callback used by deferred_to_columns(). The "target" parameter should
     
    642646        for field in fields:
    643647            target[table].add(field.column)
    644648
    645 
    646649    def table_alias(self, table_name, create=False):
    647650        """
    648651        Returns a table alias for the given table_name and whether this is a
  • django/db/models/query.py

     
    13931393                # If the related object exists, populate
    13941394                # the descriptor cache.
    13951395                setattr(rel_obj, f.get_cache_name(), obj)
    1396                 # Now populate all the non-local field values
    1397                 # on the related object
    1398                 for rel_field, rel_model in rel_obj._meta.get_fields_with_model():
     1396                # Now populate all the non-local field values on the related
     1397                # object. If this object has deferred fields, we need to use
     1398                # the opts from the original model to get non-local fields
     1399                # correctly.
     1400                opts = rel_obj._meta
     1401                if getattr(rel_obj, '_deferred'):
     1402                    opts = opts.proxy_for_model._meta
     1403                for rel_field, rel_model in opts.get_fields_with_model():
    13991404                    if rel_model is not None:
    14001405                        setattr(rel_obj, rel_field.attname, getattr(obj, rel_field.attname))
    14011406                        # populate the field cache for any related object
  • tests/regressiontests/defer_regress/tests.py

     
    99from django.test import TestCase
    1010
    1111from .models import (ResolveThis, Item, RelatedItem, Child, Leaf, Proxy,
    12     SimpleItem, Feature)
     12    SimpleItem, Feature, OneToOneItem)
    1313
    1414
    1515class DeferRegressionTest(TestCase):
     
    110110                Feature,
    111111                Item,
    112112                Leaf,
     113                OneToOneItem,
    113114                Proxy,
    114115                RelatedItem,
    115116                ResolveThis,
     
    141142                "Leaf_Deferred_name_value",
    142143                "Leaf_Deferred_second_child_value",
    143144                "Leaf_Deferred_value",
     145                "OneToOneItem",
    144146                "Proxy",
    145147                "RelatedItem",
    146148                "RelatedItem_Deferred_",
     
    174176        qs = ResolveThis.objects.defer('num')
    175177        self.assertEqual(1, qs.count())
    176178        self.assertEqual('Foobar', qs[0].name)
     179
     180    def test_reverse_one_to_one_relations(self):
     181        item = Item.objects.create(name="first", value=42)
     182        OneToOneItem.objects.create(item=item, name="second")
     183        self.assertEqual(len(Item.objects.all()), 1)
     184        self.assertEqual(len(Item.objects.defer('one_to_one_item__name')), 1)
     185        self.assertEqual(len(Item.objects.select_related('one_to_one_item')), 1)
     186        self.assertEqual(len(Item.objects.select_related('one_to_one_item').defer('one_to_one_item__name')), 1)
     187        self.assertEqual(len(Item.objects.select_related('one_to_one_item').defer('value')), 1)
  • tests/regressiontests/defer_regress/models.py

     
    4747
    4848class Feature(models.Model):
    4949    item = models.ForeignKey(SimpleItem)
     50
     51class OneToOneItem(models.Model):
     52    item = models.OneToOneField(Item, related_name="one_to_one_item")
     53    name = models.CharField(max_length=15)
Back to Top