Ticket #10695: defer_v2.diff

File defer_v2.diff, 8.4 KB (added by jbronn, 6 years ago)
  • django/db/models/base.py

     
    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:
     
    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

     
    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,
     
    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])
     
    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

     
    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
     
    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
     172        cls = self.model_ref()
     173        field = cls._meta.get_field(self.field_name)
     174        if not hasattr(instance, field.get_cache_name()):
     175            setattr(instance, field.get_cache_name(), cls._base_manager.filter(pk=instance.pk).values_list(self.field_name, flat=True).get())
     176        return getattr(instance, field.get_cache_name())
    180177
    181     def __set__(self, name, value):
     178    def __set__(self, instance, value):
    182179        """
    183180        Deferred loading attributes can be set normally (which means there will
    184181        never be a database lookup involved.
    185182        """
    186         self.value = value
    187         self.loaded = True
     183        field = self.model_ref()._meta.get_field(self.field_name)
     184        setattr(instance, field.get_cache_name(), value)
    188185
    189186def select_related_descend(field, restricted, requested):
    190187    """
     
    206203# This function is needed because data descriptors must be defined on a class
    207204# object, not an instance, to have any effect.
    208205
    209 def deferred_class_factory(model, pk_value, attrs):
     206def deferred_class_factory(model, attrs):
    210207    """
    211208    Returns a class object that is a copy of "model" with the specified "attrs"
    212209    being replaced with DeferredAttribute objects. The "pk_value" ties the
     
    223220    # are identical.
    224221    name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs))))
    225222
    226     overrides = dict([(attr, DeferredAttribute(attr, pk_value, model))
     223    overrides = dict([(attr, DeferredAttribute(attr, model))
    227224            for attr in attrs])
    228225    overrides["Meta"] = Meta
    229226    overrides["__module__"] = model.__module__
     
    233230# The above function is also used to unpickle model instances with deferred
    234231# fields.
    235232deferred_class_factory.__safe_for_unpickling__ = True
    236 
  • django/contrib/gis/tests/relatedapp/tests.py

     
    183183            self.assertEqual(m.point, d['point'])
    184184            self.assertEqual(m.point, t[1])
    185185
    186     # 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)
     186    def test08_defer_only(self):
     187        "Testing defer() and only() on Geographic models."
     188        qs = Location.objects.all()
     189        def_qs = Location.objects.defer('point')
     190        for loc, def_loc in zip(qs, def_qs):
     191            self.assertEqual(loc.point, def_loc.point)
    193192
    194193    # TODO: Related tests for KML, GML, and distance lookups.
    195194
  • tests/regressiontests/defer_regress/models.py

     
    66from django.db import connection, models
    77
    88class Item(models.Model):
    9     name = models.CharField(max_length=10)
     9    name = models.CharField(max_length=20)
    1010    text = models.TextField(default="xyzzy")
    1111    value = models.IntegerField()
    1212    other_value = models.IntegerField(default=0)
     
    4040>>> len(connection.queries) == num + 2      # Effect of text lookup.
    4141True
    4242
     43>>> _ = Item.objects.create(name="no I'm first", value=37)
     44>>> items = Item.objects.only('value').order_by('-value')
     45>>> items[0].name
     46u'first'
     47>>> items[1].name
     48u"no I'm first"
     49
    4350>>> settings.DEBUG = False
    4451
    4552"""
    4653}
    47 
Back to Top