1"""This example creates a new dogpile.cache backend that will persist data in a 2dictionary which is local to the current session. remove() the session and 3the cache is gone. 4 5Create a new Dogpile cache backend that will store 6cached data local to the current Session. 7 8This is an advanced example which assumes familiarity 9with the basic operation of CachingQuery. 10 11""" 12 13from dogpile.cache.api import CacheBackend 14from dogpile.cache.api import NO_VALUE 15from dogpile.cache.region import register_backend 16 17 18class ScopedSessionBackend(CacheBackend): 19 """A dogpile backend which will cache objects locally on 20 the current session. 21 22 When used with the query_cache system, the effect is that the objects 23 in the cache are the same as that within the session - the merge() 24 is a formality that doesn't actually create a second instance. 25 This makes it safe to use for updates of data from an identity 26 perspective (still not ideal for deletes though). 27 28 When the session is removed, the cache is gone too, so the cache 29 is automatically disposed upon session.remove(). 30 31 """ 32 33 def __init__(self, arguments): 34 self.scoped_session = arguments["scoped_session"] 35 36 def get(self, key): 37 return self._cache_dictionary.get(key, NO_VALUE) 38 39 def set(self, key, value): 40 self._cache_dictionary[key] = value 41 42 def delete(self, key): 43 self._cache_dictionary.pop(key, None) 44 45 @property 46 def _cache_dictionary(self): 47 """Return the cache dictionary linked to the current Session.""" 48 49 sess = self.scoped_session() 50 try: 51 cache_dict = sess._cache_dictionary 52 except AttributeError: 53 sess._cache_dictionary = cache_dict = {} 54 return cache_dict 55 56 57register_backend("sqlalchemy.session", __name__, "ScopedSessionBackend") 58 59 60if __name__ == "__main__": 61 from .environment import Session, regions 62 from .caching_query import FromCache 63 from dogpile.cache import make_region 64 65 # set up a region based on the ScopedSessionBackend, 66 # pointing to the scoped_session declared in the example 67 # environment. 68 regions["local_session"] = make_region().configure( 69 "sqlalchemy.session", arguments={"scoped_session": Session} 70 ) 71 72 from .model import Person 73 74 # query to load Person by name, with criterion 75 # of "person 10" 76 q = ( 77 Session.query(Person) 78 .options(FromCache("local_session")) 79 .filter(Person.name == "person 10") 80 ) 81 82 # load from DB 83 person10 = q.one() 84 85 # next call, the query is cached. 86 person10 = q.one() 87 88 # clear out the Session. The "_cache_dictionary" dictionary 89 # disappears with it. 90 Session.remove() 91 92 # query calls from DB again 93 person10 = q.one() 94 95 # identity is preserved - person10 is the *same* object that's 96 # ultimately inside the cache. So it is safe to manipulate 97 # the not-queried-for attributes of objects when using such a 98 # cache without the need to invalidate - however, any change 99 # that would change the results of a cached query, such as 100 # inserts, deletes, or modification to attributes that are 101 # part of query criterion, still require careful invalidation. 102 cache, key = q._get_cache_plus_key() 103 assert person10 is cache.get(key)[0] 104