Ticket #10695: defer_v2.diff
File defer_v2.diff, 8.4 KB (added by , 16 years ago) |
---|
-
django/db/models/base.py
362 362 # DeferredAttribute classes, so we only need to do this 363 363 # once. 364 364 obj = self.__class__.__dict__[field.attname] 365 pk_val = obj.pk_value366 365 model = obj.model_ref() 367 return (model_unpickle, (model, pk_val,defers), data)366 return (model_unpickle, (model, defers), data) 368 367 369 368 def _get_pk_val(self, meta=None): 370 369 if not meta: … … 635 634 class Empty(object): 636 635 pass 637 636 638 def model_unpickle(model, pk_val,attrs):637 def model_unpickle(model, attrs): 639 638 """ 640 639 Used to unpickle Model subclasses with deferred fields. 641 640 """ 642 641 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) 644 643 return cls.__new__(cls) 645 644 model_unpickle.__safe_for_unpickle__ = True 646 645 -
django/db/models/query.py
190 190 index_start = len(extra_select) 191 191 aggregate_start = index_start + len(self.model._meta.fields) 192 192 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 193 207 for row in self.query.results_iter(): 194 208 if fill_cache: 195 209 obj, _ = get_cached_row(self.model, row, … … 197 211 requested=requested, offset=len(aggregate_select), 198 212 only_load=only_load) 199 213 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: 204 215 row_data = row[index_start:aggregate_start] 205 216 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))) 219 218 else: 220 219 # Omit aggregates in object creation. 221 220 obj = self.model(*row[index_start:aggregate_start]) … … 927 926 else: 928 927 init_list.append(field.attname) 929 928 if skip: 930 klass = deferred_class_factory(klass, pk_val,skip)929 klass = deferred_class_factory(klass, skip) 931 930 obj = klass(**dict(zip(init_list, fields))) 932 931 else: 933 932 obj = klass(*fields) -
django/db/models/query_utils.py
158 158 A wrapper for a deferred-loading field. When the value is read from this 159 159 object the first time, the query is executed. 160 160 """ 161 def __init__(self, field_name, pk_value,model):161 def __init__(self, field_name, model): 162 162 self.field_name = field_name 163 self.pk_value = pk_value164 163 self.model_ref = weakref.ref(model) 165 164 self.loaded = False 166 165 … … 170 169 Returns the cached value. 171 170 """ 172 171 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()) 180 177 181 def __set__(self, name, value):178 def __set__(self, instance, value): 182 179 """ 183 180 Deferred loading attributes can be set normally (which means there will 184 181 never be a database lookup involved. 185 182 """ 186 self.value = value187 se lf.loaded = True183 field = self.model_ref()._meta.get_field(self.field_name) 184 setattr(instance, field.get_cache_name(), value) 188 185 189 186 def select_related_descend(field, restricted, requested): 190 187 """ … … 206 203 # This function is needed because data descriptors must be defined on a class 207 204 # object, not an instance, to have any effect. 208 205 209 def deferred_class_factory(model, pk_value,attrs):206 def deferred_class_factory(model, attrs): 210 207 """ 211 208 Returns a class object that is a copy of "model" with the specified "attrs" 212 209 being replaced with DeferredAttribute objects. The "pk_value" ties the … … 223 220 # are identical. 224 221 name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs)))) 225 222 226 overrides = dict([(attr, DeferredAttribute(attr, pk_value,model))223 overrides = dict([(attr, DeferredAttribute(attr, model)) 227 224 for attr in attrs]) 228 225 overrides["Meta"] = Meta 229 226 overrides["__module__"] = model.__module__ … … 233 230 # The above function is also used to unpickle model instances with deferred 234 231 # fields. 235 232 deferred_class_factory.__safe_for_unpickling__ = True 236 -
django/contrib/gis/tests/relatedapp/tests.py
183 183 self.assertEqual(m.point, d['point']) 184 184 self.assertEqual(m.point, t[1]) 185 185 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) 193 192 194 193 # TODO: Related tests for KML, GML, and distance lookups. 195 194 -
tests/regressiontests/defer_regress/models.py
6 6 from django.db import connection, models 7 7 8 8 class Item(models.Model): 9 name = models.CharField(max_length= 10)9 name = models.CharField(max_length=20) 10 10 text = models.TextField(default="xyzzy") 11 11 value = models.IntegerField() 12 12 other_value = models.IntegerField(default=0) … … 40 40 >>> len(connection.queries) == num + 2 # Effect of text lookup. 41 41 True 42 42 43 >>> _ = Item.objects.create(name="no I'm first", value=37) 44 >>> items = Item.objects.only('value').order_by('-value') 45 >>> items[0].name 46 u'first' 47 >>> items[1].name 48 u"no I'm first" 49 43 50 >>> settings.DEBUG = False 44 51 45 52 """ 46 53 } 47