Code

Ticket #17: instance-caching-2.patch

File instance-caching-2.patch, 20.9 KB (added by (removed), 7 years ago)

instance-caching-v2

  • django/core/serializers/python.py

    # Bazaar revision bundle v0.9
    #
    # message:
    #   update select_related tests with proper counts; basically heavy reduction of # of queries required via instance caching tricks in foreign key backref instances
    # committer: Brian Harring <ferringb@gmail.com>
    # date: Sun 2007-05-27 10:21:20.177999973 -0700
    
    === modified file django/core/serializers/python.py // last-changed:ferringb@gm
    ... ail.com-20070525002257-juow7h2cfizoox5u
     
    8989            else: 
    9090                data[field.name] = field.to_python(field_value) 
    9191                 
     92        data["disable_inst_caching"] = True 
    9293        yield base.DeserializedObject(Model(**data), m2m_data) 
    9394 
    9495def _get_model(model_identifier): 
  • django/core/serializers/xml_serializer.py

    === modified file django/core/serializers/xml_serializer.py // last-changed:fer
    ... ringb@gmail.com-20070525001853-wnodu5zd8vdcda59
     
    170170                    value = field.to_python(getInnerText(field_node).strip().encode(self.encoding)) 
    171171                data[field.name] = value 
    172172         
     173        data["disable_inst_caching"] = True 
    173174        # Return a DeserializedObject so that the m2m data has a place to live. 
    174175        return base.DeserializedObject(Model(**data), m2m_data) 
    175176         
     
    226227            inner_text.extend(getInnerText(child)) 
    227228        else: 
    228229           pass 
    229     return "".join(inner_text) 
    230  No newline at end of file 
     230    return "".join(inner_text) 
  • django/db/models/base.py

    === modified file django/db/models/base.py // last-changed:ferringb@gmail.com-2
    ... 0070525001853-wnodu5zd8vdcda59
     
    1414from django.utils.functional import curry 
    1515from django.conf import settings 
    1616from itertools import izip 
     17from weakref import WeakValueDictionary 
    1718import types 
    1819import sys 
    1920import os 
     
    7677        # registered version. 
    7778        return get_model(new_class._meta.app_label, name, False) 
    7879 
     80    def __call__(cls, *args, **kwargs): 
     81        if not kwargs.pop("disable_inst_caching", False) and cls._meta.has_auto_field: 
     82            key = cls._get_cache_key(args, kwargs) 
     83            if key is not None: 
     84                obj = cls.__instance_cache__.get(key) 
     85                if obj is None: 
     86                    obj = super(ModelBase, cls).__call__(*args, **kwargs) 
     87                    cls.__instance_cache__[key] = obj 
     88            else: 
     89                obj = super(ModelBase, cls).__call__(*args, **kwargs) 
     90        else: 
     91            obj = super(ModelBase, cls).__call__(*args, **kwargs) 
     92        return obj 
     93 
     94 
    7995class Model(object): 
    8096    __metaclass__ = ModelBase 
    8197 
     
    94110    def __ne__(self, other): 
    95111        return not self.__eq__(other) 
    96112 
     113    def _get_cache_key(cls, args, kwargs): 
     114        # this should be calculated *once*, but isn't atm 
     115        pk_position = cls._meta.fields.index(cls._meta.pk) 
     116        if len(args) > pk_position: 
     117            return args[pk_position] 
     118        pk = cls._meta.pk 
     119        if pk.name in kwargs: 
     120            return kwargs[pk.name] 
     121        elif pk.attname in kwargs: 
     122            return kwargs[pk.attname] 
     123        return None 
     124    _get_cache_key = classmethod(_get_cache_key) 
     125 
     126    def get_cached_instance(cls, id): 
     127        return cls.__instance_cache__.get(id) 
     128    get_cached_instance = classmethod(get_cached_instance) 
     129 
    97130    def __init__(self, *args, **kwargs): 
    98131        dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) 
    99132         
     
    194227        if hasattr(cls, 'get_absolute_url'): 
    195228            cls.get_absolute_url = curry(get_absolute_url, opts, cls.get_absolute_url) 
    196229 
     230        cls.__instance_cache__ = WeakValueDictionary() 
     231 
    197232        dispatcher.send(signal=signals.class_prepared, sender=cls) 
    198233 
    199234    _prepare = classmethod(_prepare) 
     
    251286                setattr(self, self._meta.pk.attname, backend.get_last_insert_id(cursor, self._meta.db_table, self._meta.pk.column)) 
    252287        transaction.commit_unless_managed() 
    253288 
     289        # if we're a new instance that hasn't been written in; save ourself. 
     290        if self._meta.has_auto_field: 
     291            self.__instance_cache__[getattr(self, self._meta.pk.attname)] = self 
     292 
    254293        # Run any post-save hooks. 
    255294        dispatcher.send(signal=signals.post_save, sender=self.__class__, instance=self) 
    256295 
  • django/db/models/fields/related.py

    === modified file django/db/models/fields/related.py // last-changed:ferringb@g
    ... mail.com-20070524150447-bxe88y4c8ixjsqqa
     
    163163                if self.field.null: 
    164164                    return None 
    165165                raise self.field.rel.to.DoesNotExist 
    166             other_field = self.field.rel.get_related_field() 
    167             if other_field.rel: 
    168                 params = {'%s__pk' % self.field.rel.field_name: val} 
    169             else: 
    170                 params = {'%s__exact' % self.field.rel.field_name: val} 
    171             rel_obj = self.field.rel.to._default_manager.get(**params) 
     166            rel_obj = self.field.rel.to.get_cached_instance(val) 
     167            if rel_obj is None: 
     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) 
    172174            setattr(instance, cache_name, rel_obj) 
    173175            return rel_obj 
    174176 
  • tests/modeltests/model_forms/models.py

    === modified file tests/modeltests/model_forms/models.py // last-changed:ferrin
    ... gb@gmail.com-20070525002908-88uk1p54t5xwec3w
     
    2442441 
    245245>>> test_art = Article.objects.get(id=1) 
    246246>>> test_art.headline 
    247 'Test headline' 
     247u'Test headline' 
    248248 
    249249You can create a form over a subset of the available fields  
    250250by specifying a 'fields' argument to form_for_instance.  
     
    2602601 
    261261>>> new_art = Article.objects.get(id=1) 
    262262>>> new_art.headline 
    263 'New headline' 
     263u'New headline' 
    264264 
    265265Add some categories and test the many-to-many form output. 
    266266>>> new_art.categories.all() 
  • tests/modeltests/select_related/models.py

    === modified file tests/modeltests/select_related/models.py
     
    1071071 
    108108 
    109109# 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) 
    111111>>> db.reset_queries() 
    112112>>> world = Species.objects.all() 
    113113>>> [o.genus.family for o in world] 
    114114[<Family: Drosophilidae>, <Family: Hominidae>, <Family: Fabaceae>, <Family: Amanitacae>] 
    115115>>> len(db.connection.queries) 
    116 9 
     1165 
    117117 
    118118# With select_related(): 
    119119>>> db.reset_queries() 
     
    129129>>> pea.genus.family.order.klass.phylum.kingdom.domain 
    130130<Domain: Eukaryota> 
    131131 
    132 # Notice: one few query than above because of depth=1 
     132# notice: instance caching saves the day; would be 7 without. 
    133133>>> len(db.connection.queries) 
    134 7 
     1341 
    135135 
    136136>>> db.reset_queries() 
    137137>>> pea = Species.objects.select_related(depth=5).get(name="sativum") 
    138138>>> pea.genus.family.order.klass.phylum.kingdom.domain 
    139139<Domain: Eukaryota> 
    140140>>> len(db.connection.queries) 
    141 3 
     1411 
    142142 
    143143>>> db.reset_queries() 
    144144>>> world = Species.objects.all().select_related(depth=2) 
    145145>>> [o.genus.family.order for o in world] 
    146146[<Order: Diptera>, <Order: Primates>, <Order: Fabales>, <Order: Agaricales>] 
    147147>>> len(db.connection.queries) 
    148 5 
     1481 
    149149 
    150150# Reset DEBUG to where we found it. 
    151151>>> settings.DEBUG = False