Ticket #17: instance-caching-4.patch
| File instance-caching-4.patch, 12.7 kB (added by Brian Harring <ferringb@gmail.com>, 1 year ago) |
|---|
-
django/core/serializers/python.py
old new 88 88 # Handle all other fields 89 89 else: 90 90 data[field.name] = field.to_python(field_value) 91 91 data["disable_inst_caching"] = True 92 92 yield base.DeserializedObject(Model(**data), m2m_data) 93 93 94 94 def _get_model(model_identifier): -
django/core/serializers/xml_serializer.py
old new 176 176 else: 177 177 value = field.to_python(getInnerText(field_node).strip()) 178 178 data[field.name] = value 179 179 data["disable_inst_caching"] = True 180 180 # Return a DeserializedObject so that the m2m data has a place to live. 181 181 return base.DeserializedObject(Model(**data), m2m_data) 182 182 … … 234 234 else: 235 235 pass 236 236 return u"".join(inner_text) 237 -
django/db/models/base.py
old new 15 15 from django.utils.encoding import smart_str, force_unicode 16 16 from django.conf import settings 17 17 from itertools import izip 18 from weakref import WeakValueDictionary 18 19 import types 19 20 import sys 20 21 import os … … 77 78 # registered version. 78 79 return get_model(new_class._meta.app_label, name, False) 79 80 81 def __call__(cls, *args, **kwargs): 82 if not kwargs.pop("disable_inst_caching", False) and cls._meta.has_auto_field: 83 key = cls._get_cache_key(args, kwargs) 84 if key is not None: 85 obj = cls.__instance_cache__.get(key) 86 if obj is None: 87 obj = super(ModelBase, cls).__call__(*args, **kwargs) 88 cls.__instance_cache__[key] = obj 89 else: 90 obj = super(ModelBase, cls).__call__(*args, **kwargs) 91 else: 92 obj = super(ModelBase, cls).__call__(*args, **kwargs) 93 return obj 94 95 80 96 class Model(object): 81 97 __metaclass__ = ModelBase 82 98 … … 97 113 def __ne__(self, other): 98 114 return not self.__eq__(other) 99 115 116 def _get_cache_key(cls, args, kwargs): 117 # this should be calculated *once*, but isn't atm 118 pk_position = cls._meta.fields.index(cls._meta.pk) 119 if len(args) > pk_position: 120 return args[pk_position] 121 pk = cls._meta.pk 122 if pk.name in kwargs: 123 return kwargs[pk.name] 124 elif pk.attname in kwargs: 125 return kwargs[pk.attname] 126 return None 127 _get_cache_key = classmethod(_get_cache_key) 128 129 def get_cached_instance(cls, id): 130 return cls.__instance_cache__.get(id) 131 get_cached_instance = classmethod(get_cached_instance) 132 100 133 def __init__(self, *args, **kwargs): 101 134 dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) 102 135 … … 197 230 if hasattr(cls, 'get_absolute_url'): 198 231 cls.get_absolute_url = curry(get_absolute_url, opts, cls.get_absolute_url) 199 232 233 cls.__instance_cache__ = WeakValueDictionary() 234 200 235 dispatcher.send(signal=signals.class_prepared, sender=cls) 201 236 202 237 _prepare = classmethod(_prepare) … … 255 290 setattr(self, self._meta.pk.attname, backend.get_last_insert_id(cursor, self._meta.db_table, self._meta.pk.column)) 256 291 transaction.commit_unless_managed() 257 292 293 # if we're a new instance that hasn't been written in; save ourself. 294 if self._meta.has_auto_field: 295 self.__instance_cache__[self._get_pk_val()] = self 296 258 297 # Run any post-save hooks. 259 298 dispatcher.send(signal=signals.post_save, sender=self.__class__, instance=self) 260 299 … … 315 354 self._collect_sub_objects(seen_objs) 316 355 317 356 # Actually delete the objects 357 if self._meta.has_auto_field: 358 pk = self._get_pk_val() 359 if pk is not None: 360 self.__instance_cache__.pop(pk, None) 318 361 delete_objects(seen_objs) 319 362 320 363 delete.alters_data = True -
django/db/models/fields/related.py
old new 165 165 if self.field.null: 166 166 return None 167 167 raise self.field.rel.to.DoesNotExist 168 other_field = self.field.rel.get_related_field() 169 if other_field.rel: 170 params = {'%s__pk' % self.field.rel.field_name: val} 171 else: 172 params = {'%s__exact' % self.field.rel.field_name: val} 173 rel_obj = self.field.rel.to._default_manager.get(**params) 168 rel_obj = self.field.rel.to.get_cached_instance(val) 169 if rel_obj is None: 170 other_field = self.field.rel.get_related_field() 171 if other_field.rel: 172 params = {'%s__pk' % self.field.rel.field_name: val} 173 else: 174 params = {'%s__exact' % self.field.rel.field_name: val} 175 rel_obj = self.field.rel.to._default_manager.get(**params) 174 176 setattr(instance, cache_name, rel_obj) 175 177 return rel_obj 176 178 -
django/db/models/query.py
old new 1109 1109 for cls in ordered_classes: 1110 1110 seen_objs[cls] = seen_objs[cls].items() 1111 1111 seen_objs[cls].sort() 1112 clean_inst_cache = cls.__instance_cache__.pop 1112 1113 1113 1114 # Pre notify all instances to be deleted 1114 1115 for pk_val, instance in seen_objs[cls]: 1115 1116 dispatcher.send(signal=signals.pre_delete, sender=cls, instance=instance) 1116 1117 1117 1118 pk_list = [pk for pk,instance in seen_objs[cls]] 1119 # we wipe the cache now; it's *possible* some form of a __get__ lookup may reintroduce an item after 1120 # the fact with the same pk (extremely unlikely) 1121 for x in pk_list: 1122 clean_inst_cache(x, None) 1123 1118 1124 for related in cls._meta.get_all_related_many_to_many_objects(): 1119 1125 if not isinstance(related.field, generic.GenericRelation): 1120 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):1126 for offset in xrange(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 1121 1127 cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ 1122 1128 (qn(related.field.m2m_db_table()), 1123 1129 qn(related.field.m2m_reverse_name()), 1124 ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), 1125 pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) 1130 ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), 1131 pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) 1132 1126 1133 for f in cls._meta.many_to_many: 1127 1134 if isinstance(f, generic.GenericRelation): 1128 1135 from django.contrib.contenttypes.models import ContentType … … 1131 1138 else: 1132 1139 query_extra = '' 1133 1140 args_extra = [] 1134 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):1141 for offset in xrange(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 1135 1142 cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \ 1136 1143 (qn(f.m2m_db_table()), qn(f.m2m_column_name()), 1137 1144 ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra, 1138 1145 pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra) 1146 1139 1147 for field in cls._meta.fields: 1140 1148 if field.rel and field.null and field.rel.to in seen_objs: 1141 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):1149 for offset in xrange(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 1142 1150 cursor.execute("UPDATE %s SET %s=NULL WHERE %s IN (%s)" % \ 1143 1151 (qn(cls._meta.db_table), qn(field.column), qn(cls._meta.pk.column), 1144 1152 ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), … … 1148 1156 for cls in ordered_classes: 1149 1157 seen_objs[cls].reverse() 1150 1158 pk_list = [pk for pk,instance in seen_objs[cls]] 1151 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 1159 clean_inst_cache = cls.__instance_cache__.pop 1160 for x in pk_list: 1161 clean_inst_cache(x, None) 1162 for offset in xrange(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 1152 1163 cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ 1153 1164 (qn(cls._meta.db_table), qn(cls._meta.pk.column), 1154 1165 ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), -
tests/modeltests/basic/models.py
old new 348 348 __test__['API_TESTS'] += """ 349 349 350 350 # You can manually specify the primary key when creating a new object. 351 >>> a101 = Article(id=101, headline= 'Article 101', pub_date=datetime(2005, 7, 31, 12, 30, 45))351 >>> a101 = Article(id=101, headline=u'Article 101', pub_date=datetime(2005, 7, 31, 12, 30, 45)) 352 352 >>> a101.save() 353 353 >>> a101 = Article.objects.get(pk=101) 354 354 >>> a101.headline -
tests/modeltests/custom_columns/models.py
old new 40 40 41 41 __test__ = {'API_TESTS':""" 42 42 # Create a Author. 43 >>> a = Author(first_name= 'John', last_name='Smith')43 >>> a = Author(first_name=u'John', last_name=u'Smith') 44 44 >>> a.save() 45 45 46 46 >>> a.id -
tests/modeltests/generic_relations/models.py
old new 87 87 88 88 # Recall that the Mineral class doesn't have an explicit GenericRelation 89 89 # defined. That's OK, because you can create TaggedItems explicitly. 90 >>> tag1 = TaggedItem(content_object=quartz, tag= "shiny")91 >>> tag2 = TaggedItem(content_object=quartz, tag= "clearish")90 >>> tag1 = TaggedItem(content_object=quartz, tag=u"shiny") 91 >>> tag2 = TaggedItem(content_object=quartz, tag=u"clearish") 92 92 >>> tag1.save() 93 93 >>> tag2.save() 94 94 -
tests/modeltests/many_to_one/models.py
old new 27 27 28 28 __test__ = {'API_TESTS':""" 29 29 # Create a few Reporters. 30 >>> r = Reporter(first_name= 'John', last_name='Smith', email='john@example.com')30 >>> r = Reporter(first_name=u'John', last_name=u'Smith', email='john@example.com') 31 31 >>> r.save() 32 32 33 >>> r2 = Reporter(first_name= 'Paul', last_name='Jones', email='paul@example.com')33 >>> r2 = Reporter(first_name=u'Paul', last_name=u'Jones', email='paul@example.com') 34 34 >>> r2.save() 35 35 36 36 # Create an Article. -
tests/modeltests/select_related/models.py
old new 107 107 1 108 108 109 109 # select_related() also of course applies to entire lists, not just items. 110 # Without select_related() 110 # Without select_related() (note instance caching still reduces this from 9 to 5) 111 111 >>> db.reset_queries() 112 112 >>> world = Species.objects.all() 113 113 >>> [o.genus.family for o in world] 114 114 [<Family: Drosophilidae>, <Family: Hominidae>, <Family: Fabaceae>, <Family: Amanitacae>] 115 115 >>> len(db.connection.queries) 116 9 116 5 117 117 118 118 # With select_related(): 119 119 >>> db.reset_queries() … … 129 129 >>> pea.genus.family.order.klass.phylum.kingdom.domain 130 130 <Domain: Eukaryota> 131 131 132 # Notice: one few query than above because of depth=1132 # notice: instance caching saves the day; would be 7 without. 133 133 >>> len(db.connection.queries) 134 7 134 1 135 135 136 136 >>> db.reset_queries() 137 137 >>> pea = Species.objects.select_related(depth=5).get(name="sativum") 138 138 >>> pea.genus.family.order.klass.phylum.kingdom.domain 139 139 <Domain: Eukaryota> 140 140 >>> len(db.connection.queries) 141 3 141 1 142 142 143 143 >>> db.reset_queries() 144 144 >>> world = Species.objects.all().select_related(depth=2) 145 145 >>> [o.genus.family.order for o in world] 146 146 [<Order: Diptera>, <Order: Primates>, <Order: Fabales>, <Order: Agaricales>] 147 147 >>> len(db.connection.queries) 148 5 148 1 149 149 150 150 # Reset DEBUG to where we found it. 151 151 >>> settings.DEBUG = False
