Ticket #10695: defer.6.diff

File defer.6.diff, 9.2 KB (added by Alex, 6 years ago)
  • django/contrib/gis/tests/relatedapp/tests.py

    diff --git a/django/contrib/gis/tests/relatedapp/tests.py b/django/contrib/gis/tests/relatedapp/tests.py
    index 3d162b0..acc616c 100644
    a b class RelatedGeoModelTest(unittest.TestCase): 
    184184            self.assertEqual(m.point, t[1])
    185185
    186186    # Test disabled until #10572 is resolved.
    187     #def test08_defer_only(self):
    188     #    "Testing defer() and only() on Geographic models."
    189     #    qs = Location.objects.all()
    190     #    def_qs = Location.objects.defer('point')
    191     #    for loc, def_loc in zip(qs, def_qs):
    192     #        self.assertEqual(loc.point, def_loc.point)
     187    def test08_defer_only(self):
     188        "Testing defer() and only() on Geographic models."
     189        qs = Location.objects.all()
     190        def_qs = Location.objects.defer('point')
     191        for loc, def_loc in zip(qs, def_qs):
     192            self.assertEqual(loc.point, def_loc.point)
    193193
    194194    # TODO: Related tests for KML, GML, and distance lookups.
    195195
  • django/db/models/base.py

    diff --git a/django/db/models/base.py b/django/db/models/base.py
    index 01e2ca7..05cd0d9 100644
    a b class Model(object): 
    362362                    # DeferredAttribute classes, so we only need to do this
    363363                    # once.
    364364                    obj = self.__class__.__dict__[field.attname]
    365                     pk_val = obj.pk_value
    366365                    model = obj.model_ref()
    367         return (model_unpickle, (model, pk_val, defers), data)
     366        return (model_unpickle, (model, defers), data)
    368367
    369368    def _get_pk_val(self, meta=None):
    370369        if not meta:
    def get_absolute_url(opts, func, self, *args, **kwargs): 
    635634class Empty(object):
    636635    pass
    637636
    638 def model_unpickle(model, pk_val, attrs):
     637def model_unpickle(model, attrs):
    639638    """
    640639    Used to unpickle Model subclasses with deferred fields.
    641640    """
    642641    from django.db.models.query_utils import deferred_class_factory
    643     cls = deferred_class_factory(model, pk_val, attrs)
     642    cls = deferred_class_factory(model, attrs)
    644643    return cls.__new__(cls)
    645644model_unpickle.__safe_for_unpickle__ = True
    646645
  • django/db/models/query.py

    diff --git a/django/db/models/query.py b/django/db/models/query.py
    index ea7129b..9dcc031 100644
    a b class QuerySet(object): 
    190190        index_start = len(extra_select)
    191191        aggregate_start = index_start + len(self.model._meta.fields)
    192192
     193        load_fields = only_load.get(self.model)
     194        skip = None
     195        if load_fields and not fill_cache:
     196            # Some fields have been deferred, so we have to initialise
     197            # via keyword arguments.
     198            skip = set()
     199            init_list = []
     200            for field in fields:
     201                if field.name not in load_fields:
     202                    skip.add(field.attname)
     203                else:
     204                    init_list.append(field.attname)
     205            model_cls = deferred_class_factory(self.model, skip)
     206
    193207        for row in self.query.results_iter():
    194208            if fill_cache:
    195209                obj, _ = get_cached_row(self.model, row,
    class QuerySet(object): 
    197211                            requested=requested, offset=len(aggregate_select),
    198212                            only_load=only_load)
    199213            else:
    200                 load_fields = only_load.get(self.model)
    201                 if load_fields:
    202                     # Some fields have been deferred, so we have to initialise
    203                     # via keyword arguments.
     214                if skip:
    204215                    row_data = row[index_start:aggregate_start]
    205216                    pk_val = row_data[pk_idx]
    206                     skip = set()
    207                     init_list = []
    208                     for field in fields:
    209                         if field.name not in load_fields:
    210                             skip.add(field.attname)
    211                         else:
    212                             init_list.append(field.attname)
    213                     if skip:
    214                         model_cls = deferred_class_factory(self.model, pk_val,
    215                                 skip)
    216                         obj = model_cls(**dict(zip(init_list, row_data)))
    217                     else:
    218                         obj = self.model(*row[index_start:aggregate_start])
     217                    obj = model_cls(**dict(zip(init_list, row_data)))
    219218                else:
    220219                    # Omit aggregates in object creation.
    221220                    obj = self.model(*row[index_start:aggregate_start])
    def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0, 
    927926                else:
    928927                    init_list.append(field.attname)
    929928            if skip:
    930                 klass = deferred_class_factory(klass, pk_val, skip)
     929                klass = deferred_class_factory(klass, skip)
    931930                obj = klass(**dict(zip(init_list, fields)))
    932931            else:
    933932                obj = klass(*fields)
  • django/db/models/query_utils.py

    diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
    index 8baa654..2a84c35 100644
    a b class DeferredAttribute(object): 
    158158    A wrapper for a deferred-loading field. When the value is read from this
    159159    object the first time, the query is executed.
    160160    """
    161     def __init__(self, field_name, pk_value, model):
     161    def __init__(self, field_name, model):
    162162        self.field_name = field_name
    163         self.pk_value = pk_value
    164163        self.model_ref = weakref.ref(model)
    165164        self.loaded = False
    166165
    class DeferredAttribute(object): 
    170169        Returns the cached value.
    171170        """
    172171        assert instance is not None
    173         if not self.loaded:
    174             obj = self.model_ref()
    175             if obj is None:
    176                 return
    177             self.value = list(obj._base_manager.filter(pk=self.pk_value).values_list(self.field_name, flat=True))[0]
    178             self.loaded = True
    179         return self.value
    180 
    181     def __set__(self, name, value):
     172        cls = self.model_ref()
     173        if self.field_name not in instance.__dict__:
     174            instance.__dict__[self.field_name] =  cls._base_manager.filter(pk=instance.pk).values_list(self.field_name, flat=True).get()
     175        return instance.__dict__[self.field_name]
     176
     177    def __set__(self, instance, value):
    182178        """
    183179        Deferred loading attributes can be set normally (which means there will
    184180        never be a database lookup involved.
    185181        """
    186         self.value = value
    187         self.loaded = True
     182        instance.__dict__[self.field_name] = value
    188183
    189184def select_related_descend(field, restricted, requested):
    190185    """
    def select_related_descend(field, restricted, requested): 
    206201# This function is needed because data descriptors must be defined on a class
    207202# object, not an instance, to have any effect.
    208203
    209 def deferred_class_factory(model, pk_value, attrs):
     204def deferred_class_factory(model, attrs):
    210205    """
    211206    Returns a class object that is a copy of "model" with the specified "attrs"
    212207    being replaced with DeferredAttribute objects. The "pk_value" ties the
    def deferred_class_factory(model, pk_value, attrs): 
    223218    # are identical.
    224219    name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs))))
    225220
    226     overrides = dict([(attr, DeferredAttribute(attr, pk_value, model))
     221    overrides = dict([(attr, DeferredAttribute(attr, model))
    227222            for attr in attrs])
    228223    overrides["Meta"] = Meta
    229224    overrides["__module__"] = model.__module__
    def deferred_class_factory(model, pk_value, attrs): 
    233228# The above function is also used to unpickle model instances with deferred
    234229# fields.
    235230deferred_class_factory.__safe_for_unpickling__ = True
    236 
  • tests/regressiontests/defer_regress/models.py

    diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py
    index c46d7ce..5f51513 100644
    a b from django.conf import settings 
    66from django.db import connection, models
    77
    88class Item(models.Model):
    9     name = models.CharField(max_length=10)
     9    name = models.CharField(max_length=15)
    1010    text = models.TextField(default="xyzzy")
    1111    value = models.IntegerField()
    1212    other_value = models.IntegerField(default=0)
    class Item(models.Model): 
    1414    def __unicode__(self):
    1515        return self.name
    1616
     17class RelatedItem(models.Model):
     18    item = models.ForeignKey(Item)
     19
    1720__test__ = {"regression_tests": """
    1821Deferred fields should really be deferred and not accidentally use the field's
    1922default value just because they aren't passed to __init__.
    True 
    3942u"xyzzy"
    4043>>> len(connection.queries) == num + 2      # Effect of text lookup.
    4144True
     45>>> obj.text
     46u"xyzzy"
     47>>> len(connection.queries) == num + 2
     48True
     49
     50>>> i = Item.objects.create(name="no I'm first", value=37)
     51>>> items = Item.objects.only('value').order_by('-value')
     52>>> items[0].name
     53u'first'
     54>>> items[1].name
     55u"no I'm first"
     56
     57>>> _ = RelatedItem.objects.create(item=i)
     58>>> r = RelatedItem.objects.defer('item').get()
     59>>> r.item_id == i.id
     60True
     61>>> r.item == i
     62True
    4263
    4364>>> settings.DEBUG = False
    4465
    4566"""
    4667}
    47 
Back to Top