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

File 14694-defer-reverse-relations-r17462.diff, 6.0 KB (added by mrmachine, 4 years ago)
  • 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)
     188        # make sure that `only()` doesn't break when we pass in a reverse relation, rather than a field on the relation.
     189        self.assertEqual(len(Item.objects.only('one_to_one_item')), 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)
  • django/db/models/sql/query.py

     
    1717from django.db.models.expressions import ExpressionNode
    1818from django.db.models.fields import FieldDoesNotExist
    1919from django.db.models.query_utils import InvalidQuery
     20from django.db.models.related import RelatedObject
    2021from django.db.models.sql import aggregates as base_aggregates_module
    2122from django.db.models.sql.constants import *
    2223from django.db.models.sql.datastructures import EmptyResultSet, Empty, MultiJoin
     
    586587            for name in parts[:-1]:
    587588                old_model = cur_model
    588589                source = opts.get_field_by_name(name)[0]
    589                 cur_model = opts.get_field_by_name(name)[0].rel.to
     590                if isinstance(source, RelatedObject):
     591                    cur_model = source.model
     592                else:
     593                    cur_model = source.rel.to
    590594                opts = cur_model._meta
    591595                # Even if we're "just passing through" this model, we must add
    592596                # both the current model's pk and the related reference field
    593                 # to the things we select.
    594                 must_include[old_model].add(source)
     597                # (if it's not a reverse relation) to the things we select.
     598                if not isinstance(source, RelatedObject):
     599                    must_include[old_model].add(source)
    595600                add_to_dict(must_include, cur_model, opts.pk)
    596601            field, model, _, _ = opts.get_field_by_name(parts[-1])
    597602            if model is None:
    598603                model = cur_model
    599             add_to_dict(seen, model, field)
     604            if not isinstance(field, RelatedObject):
     605                add_to_dict(seen, model, field)
    600606
    601607        if defer:
    602608            # We need to load all fields for each model, except those that
     
    636642            for model, values in seen.iteritems():
    637643                callback(target, model, values)
    638644
    639 
    640645    def deferred_to_columns_cb(self, target, model, fields):
    641646        """
    642647        Callback used by deferred_to_columns(). The "target" parameter should
     
    648653        for field in fields:
    649654            target[table].add(field.column)
    650655
    651 
    652656    def table_alias(self, table_name, create=False):
    653657        """
    654658        Returns a table alias for the given table_name and whether this is a
  • django/db/models/query.py

     
    14131413                # If the related object exists, populate
    14141414                # the descriptor cache.
    14151415                setattr(rel_obj, f.get_cache_name(), obj)
    1416                 # Now populate all the non-local field values
    1417                 # on the related object
    1418                 for rel_field, rel_model in rel_obj._meta.get_fields_with_model():
     1416                # Now populate all the non-local field values on the related
     1417                # object. If this object has deferred fields, we need to use
     1418                # the opts from the original model to get non-local fields
     1419                # correctly.
     1420                opts = rel_obj._meta
     1421                if getattr(rel_obj, '_deferred'):
     1422                    opts = opts.proxy_for_model._meta
     1423                for rel_field, rel_model in opts.get_fields_with_model():
    14191424                    if rel_model is not None:
    14201425                        setattr(rel_obj, rel_field.attname, getattr(obj, rel_field.attname))
    14211426                        # populate the field cache for any related object
Back to Top