Skip to content

Commit 826231f

Browse files
committed
🆕 Version 1.0.0
Added concept of namespace keys for versioning & the ability to group like keys (for invalidation purposes) group them with a namespace key. This is likely the last updates to this code before I start using it in a production environment.
1 parent 2bf75bd commit 826231f

File tree

3 files changed

+41
-11
lines changed

3 files changed

+41
-11
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Flask-SQLAlchemy-Caching
33

44
This is a fork of (iurisilvio's Flask-SQLAlchemy-Cache)[https://github.com/iurisilvio/Flask-SQLAlchemy-Cache]
55

6-
A CachingQuery implementation to Flask using Flask-SQLAlchemy and Flask-Cache.
6+
A CachingQuery implementation to Flask using Flask-SQLAlchemy and Flask-Caching.
77

88
To start using caching queries, you just have to replace Flask-SQLAlchemy `Model.query_class`.
99

flask_sqlalchemy_caching/core.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# coding: utf-8
22
"""Flask-SQLAlchemy-Cache
33
4-
A SQLAlchemy CachingQuery implementation for Flask, using Flask-Cache.
4+
A SQLAlchemy CachingQuery implementation for Flask, using Flask-Caching
55
66
It is based in SQLAlchemy docs example:
77
http://docs.sqlalchemy.org/en/latest/orm/examples.html#module-examples.dogpile_caching
88
"""
9+
import random
10+
import time
911
from hashlib import md5
1012

1113
from sqlalchemy.orm.interfaces import MapperOption
@@ -51,16 +53,37 @@ def create_func(): return list(BaseQuery.__iter__(self))
5153

5254
def _get_cache_plus_key(self):
5355
"""Return a cache region plus key."""
54-
key = getattr(self, '_cache_key', self.key_from_query())
55-
return self._cache.cache, key
56+
return self._cache.cache, self._get_key()
57+
58+
def _get_key(self, ignore_ns=False):
59+
"""Get the cache key."""
60+
key = getattr(self._cache, 'cache_key', None)
61+
if key is None:
62+
key = self.key_from_query()
63+
64+
if self._cache.namespace_key and not ignore_ns:
65+
ns_val = self._cache.cache.get(self._cache.namespace_key)
66+
if not ns_val:
67+
# If the namespace key doesn't exist, create it. But start at the current
68+
# timestamp plus milliseconds and a 3 digit random number to avoid race
69+
# conditions.
70+
ns_val = int(time.time() * 1000 + random.randint(0, 999))
71+
self._cache.cache.set(self._cache.namespace_key, ns_val)
72+
key = 'v{}.{}'.format(ns_val, key)
73+
74+
return key
5675

5776
def invalidate(self):
5877
"""Invalidate the cache value represented by this Query."""
5978
cache, cache_key = self._get_cache_plus_key()
6079
cache.delete(cache_key)
80+
if self._cache.namespace_key:
81+
cache.incr(self._cache.namespace_key)
82+
# Also invalidate if the key exists without a namespace version prepended
83+
cache.delete(self._get_key(ignore_ns=True))
6184

62-
def get_value(self, merge=True, createfunc=None,
63-
expiration_time=None, ignore_expiration=False):
85+
def get_value(self, merge=True, createfunc=None, expiration_time=None,
86+
ignore_expiration=False):
6487
"""
6588
Return the value from the cache for this query.
6689
"""
@@ -112,23 +135,30 @@ def key_from_query(self, qualifier=None):
112135

113136
class _CacheableMapperOption(MapperOption):
114137

115-
def __init__(self, cache, cache_key=None):
138+
def __init__(self, cache, cache_key=None, namespace_key=None):
116139
"""
117140
Construct a new `_CacheableMapperOption`.
118141
119-
:param cache: the cache. Should be a Flask-Cache instance.
142+
:param cache: the cache. Should be a Flask-Caching instance.
120143
121144
:param cache_key: optional. A string cache key that will serve as
122145
the key to the query. Use this if your query has a huge amount of
123146
parameters (such as when using in_()) which correspond more simply to
124147
some other identifier.
148+
149+
:param namespace_key: optional. A string cache key that can be used
150+
to group like cache keys. For example, if you are using offset & limit
151+
at the db level to paginate rows returned, and a new row is added,
152+
instead of invalidating all keys individually, only the namespace_key
153+
needs invalidated by incrementing.
125154
"""
126155
self.cache = cache
127156
self.cache_key = cache_key
157+
self.namespace_key = namespace_key
128158

129159
def __getstate__(self):
130160
"""
131-
Flask-Cache instance is not picklable because it has references
161+
Flask-Caching instance is not picklable because it has references
132162
to Flask.app. Also, I don't want it cached.
133163
"""
134164
d = self.__dict__.copy()

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
setup(
66
name='Flask-SQLAlchemy-Caching',
7-
version='0.1.1',
7+
version='1.0.0',
88
description='CachingQuery implementation to Flask using Flask-SQLAlchemy and Flask-Caching',
99
author='Brad Belyeu',
1010
author_email='bradleylamar@gmail.com',
1111
url='http://www.github.com/bbelyeu/Flask-SQLAlchemy-Caching',
12-
download_url='https://github.com/bbelyeu/Flask-SQLAlchemy-Caching/archive/0.1.1.zip',
12+
download_url='https://github.com/bbelyeu/Flask-SQLAlchemy-Caching/archive/1.0.0.zip',
1313
license='MIT',
1414
platforms='any',
1515
packages=['flask_sqlalchemy_caching'],

0 commit comments

Comments
 (0)