Django

Code

Ticket #17: 17_no_tests.diff

File 17_no_tests.diff, 10.8 kB (added by PhiR, 1 year ago)

cleanup and comments. Not working but comments welcome

  • django/db/models/base.py

    old new  
    1515from django.utils.encoding import smart_str, force_unicode, smart_unicode 
    1616from django.conf import settings 
    1717from itertools import izip 
     18from weakref import WeakValueDictionary 
    1819import types 
    1920import sys 
    2021import os 
     
    7778        # registered version. 
    7879        return get_model(new_class._meta.app_label, name, False) 
    7980 
     81    def __call__(cls, *args, **kwargs): 
     82        """ 
     83        this method will either create an instance (by calling the default implementation) 
     84        or try to retrieve one from the class-wide cache by infering the pk value from  
     85        args and kwargs. If instance caching is enabled for this class, the cache is  
     86        populated whenever possible (ie when it is possible to infer the pk value).  
     87        """ 
     88        def new_instance(): 
     89            return super(ModelBase, cls).__call__(*args, **kwargs) 
     90         
     91        # we always pop those settings from kwargs not to pollute the instance 
     92        instance_caching_enabled = kwargs.pop('meta__instance_caching', False) or cls.meta__instance_caching 
     93        # simplest case, just create a new instance every time  
     94        if not instance_caching_enabled: 
     95            return new_instance() 
     96         
     97        instance_key = cls._get_cache_key(args, kwargs) 
     98        # depending on the arguments, we might not be able to infer the PK, so in that case we create a new instance 
     99        if instance_key is None: 
     100            return new_instance() 
     101 
     102        cached_instance = cls.get_cached_instance(key) 
     103        if cached_instance is None: 
     104            cached_instance = new_instance() 
     105            # FIXME test cached_instance._get_pk_val() ==  instance_key  
     106            cls.cache_instance(cached_instance) 
     107             
     108        return cached_instance 
     109 
    80110class Model(object): 
    81111    __metaclass__ = ModelBase 
    82112 
     
    97127    def __ne__(self, other): 
    98128        return not self.__eq__(other) 
    99129 
     130    def _get_cache_key(cls, args, kwargs): 
     131        result = None 
     132        pk = cls._meta.pk 
     133        # get the index of the pk in the class fields. this should be calculated *once*, but isn't atm 
     134        pk_position = cls._meta.fields.index(pk) 
     135        # if it's in the args, we can get it easily by index 
     136        if len(args) > pk_position: 
     137            result = args[pk_position] 
     138        # retrieve the pk value. Note that we use attname instead of name, to handle the case where the pk is a  
     139        # a ForeignKey. 
     140        elif pk.attname in kwargs: 
     141            result = kwargs[pk.attname] 
     142        # ok we couldn't find the value, but maybe it's a FK and we can find the corresponding object instead 
     143        elif pk.name != pk.attname and pk.name in kwargs: 
     144            result = kwargs[pk.name] 
     145        # if the pk value happens to be a model (which can happen wich a FK), we'd rather use its own pk as the key 
     146        if result is not None and isinstance(result, Model): 
     147            result = result._get_pk_val() 
     148        return result 
     149    _get_cache_key = classmethod(_get_cache_key) 
     150 
     151    def get_cached_instance(cls, id): 
     152        """ 
     153        Method to retrieve a cached instance by pk value. Returns None when not found (which will always be the case when caching is disabled for this class). 
     154        """ 
     155        return cls.__instance_cache__.get(id) 
     156    get_cached_instance = classmethod(get_cached_instance) 
     157 
     158    def cache_instance(cls, instance): 
     159        """ 
     160        Method to store an instance in the cache. The instance will only be stored if 'instance.meta__instance_cache' is 'True', which means it is  
     161        possible to override the class-wide settings in the instance.  
     162        """ 
     163        if instance.meta__instance_cache and instance._get_pk_val() is not None: 
     164            cls.__instance_cache__[instance._get_pk_val()] = instance 
     165    cache_instance = classmethod(cache_instance) 
     166 
     167    def flush_cached_instance(cls, instance): 
     168        """ 
     169        Method to flush an instance from the cache. The instance will always be flushed from the cache, since this is most likely called from delete(). 
     170        We do not test the pk value because delete() does it and it will fail silently anyway.  
     171        """ 
     172        self.__instance_cache__.pop(_get_pk_val(), None) 
     173 
    100174    def __init__(self, *args, **kwargs): 
    101175        dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) 
    102176 
     
    197271        if hasattr(cls, 'get_absolute_url'): 
    198272            cls.get_absolute_url = curry(get_absolute_url, opts, cls.get_absolute_url) 
    199273 
     274        cls.__instance_cache__ = WeakValueDictionary() 
     275        # enable the cache according to user preferences (off by default) 
     276        cls.meta__instance_caching = getattr(cls, 'meta__instance_caching', False) 
     277 
    200278        dispatcher.send(signal=signals.class_prepared, sender=cls) 
    201279 
    202280    _prepare = classmethod(_prepare) 
     
    260338                setattr(self, self._meta.pk.attname, connection.ops.last_insert_id(cursor, self._meta.db_table, self._meta.pk.column)) 
    261339        transaction.commit_unless_managed() 
    262340 
     341        # if we're a new instance that hasn't been written in; save ourself. 
     342        self.__class__.cache_instance(self) 
     343 
    263344        # Run any post-save hooks. 
    264345        dispatcher.send(signal=signals.post_save, sender=self.__class__, instance=self) 
    265346 
     
    319400        seen_objs = SortedDict() 
    320401        self._collect_sub_objects(seen_objs) 
    321402 
     403        # remove ourself from the cache 
     404        self.__class__.flush_cached_instance(self) 
    322405        # Actually delete the objects 
    323406        delete_objects(seen_objs) 
    324407 
  • django/db/models/fields/related.py

    old new  
    165165                if self.field.null: 
    166166                    return None 
    167167                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            # try to get a cached instance, and if that fails retrieve it from the db  
     169            rel_obj = self.field.rel.to.get_cached_instance(val) 
     170            if rel_obj is None: 
     171                other_field = self.field.rel.get_related_field() 
     172                if other_field.rel: 
     173                    params = {'%s__pk' % self.field.rel.field_name: val} 
     174                else: 
     175                    params = {'%s__exact' % self.field.rel.field_name: val} 
     176                rel_obj = self.field.rel.to._default_manager.get(**params) 
    174177            setattr(instance, cache_name, rel_obj) 
    175178            return rel_obj 
    176179 
  • django/db/models/query.py

    old new  
    11341134            dispatcher.send(signal=signals.pre_delete, sender=cls, instance=instance) 
    11351135 
    11361136        pk_list = [pk for pk,instance in seen_objs[cls]] 
     1137        # we wipe the cache now; it's *possible* some form of a __get__ lookup may reintroduce an item after 
     1138        # the fact with the same pk (extremely unlikely) 
     1139        for instance in seen_objs.values(): 
     1140            cls.flush_cached_instance(instance) 
     1141 
    11371142        for related in cls._meta.get_all_related_many_to_many_objects(): 
    11381143            if not isinstance(related.field, generic.GenericRelation): 
    11391144                for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
     
    11671172    for cls in ordered_classes: 
    11681173        seen_objs[cls].reverse() 
    11691174        pk_list = [pk for pk,instance in seen_objs[cls]] 
     1175        for instance in seen_objs.values(): 
     1176            cls.flush_cached_instance(instance) 
    11701177        for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
    11711178            cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ 
    11721179                (qn(cls._meta.db_table), qn(cls._meta.pk.column), 
  • django/core/serializers/xml_serializer.py

    old new  
    176176                else: 
    177177                    value = field.to_python(getInnerText(field_node).strip()) 
    178178                data[field.name] = value 
    179  
     179        data["meta__instance_cache"] = False 
    180180        # Return a DeserializedObject so that the m2m data has a place to live. 
    181181        return base.DeserializedObject(Model(**data), m2m_data) 
    182182 
     
    234234        else: 
    235235           pass 
    236236    return u"".join(inner_text) 
    237  
  • django/core/serializers/python.py

    old new  
    8888            # Handle all other fields 
    8989            else: 
    9090                data[field.name] = field.to_python(field_value) 
    91  
     91        data["meta__instance_cache"] = False 
    9292        yield base.DeserializedObject(Model(**data), m2m_data) 
    9393 
    9494def _get_model(model_identifier): 
  • tests/modeltests/select_related/models.py

    old new  
    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 
     116
    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 
     134
    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 
     141
    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 
     148
    149149 
    150150# Reset DEBUG to where we found it. 
    151151>>> settings.DEBUG = False