Version 0.1.0.
This commit is contained in:
commit
53f8078a55
|
@ -0,0 +1,7 @@
|
|||
__pycache__/
|
||||
*.pyc
|
||||
*.so
|
||||
build/
|
||||
dist/
|
||||
sdist/
|
||||
*.egg-info/
|
|
@ -0,0 +1,6 @@
|
|||
Authors
|
||||
=======
|
||||
|
||||
================ ==========================
|
||||
Bertrand Bordage bordage.bertrand@gmail.com
|
||||
================ ==========================
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2014, Bertrand Bordage
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of django-cachalot nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,39 @@
|
|||
Django-cachalot
|
||||
===============
|
||||
|
||||
Caches your Django ORM queries and automatically invalidates them.
|
||||
|
||||
**In alpha, do not use for production**
|
||||
|
||||
.. image:: https://raw.github.com/BertrandBordage/django-cachalot/master/django-cachalot.jpg
|
||||
|
||||
|
||||
Quick start
|
||||
-----------
|
||||
|
||||
Requirements
|
||||
............
|
||||
|
||||
Django-cachalot currently requires Django 1.6
|
||||
and `django-redis <https://github.com/niwibe/django-redis>`_ as your default
|
||||
cache backend. It should work with both Python 2 & 3.
|
||||
|
||||
Usage
|
||||
.....
|
||||
|
||||
#. `pip install -e git+https://github.com/BertrandBordage/django-cachalot#egg=django-cachalot`
|
||||
#. Add ``'cachalot',`` to your ``INSTALLED_APPS``
|
||||
#. Enjoy!
|
||||
|
||||
|
||||
What still needs to be done
|
||||
---------------------------
|
||||
|
||||
- Correctly invalidate ``.extra`` queries
|
||||
- Handle transactions
|
||||
- Handle multiple database
|
||||
- Write tests, including multi-table inheritance, prefetch_related, etc
|
||||
- Find out if it’s thread-safe and test it
|
||||
- Add a ``CACHALOT_ENABLED`` setting
|
||||
- Add a setting to choose a cache other than ``'default'``
|
||||
- Add support for other caches like memcached
|
|
@ -0,0 +1,2 @@
|
|||
__version__ = (0, 1, 0)
|
||||
version_string = '.'.join(str(n) for n in __version__)
|
|
@ -0,0 +1,5 @@
|
|||
from .monkey_patch import is_patched, monkey_patch_orm
|
||||
|
||||
|
||||
if not is_patched():
|
||||
monkey_patch_orm()
|
|
@ -0,0 +1,101 @@
|
|||
# coding: utf-8
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from collections import Iterable
|
||||
from django.core.cache import cache
|
||||
from django.db.models.query import EmptyResultSet
|
||||
from django.db.models.sql.compiler import (
|
||||
SQLCompiler, SQLAggregateCompiler, SQLDateCompiler, SQLDateTimeCompiler,
|
||||
SQLInsertCompiler, SQLUpdateCompiler, SQLDeleteCompiler)
|
||||
|
||||
|
||||
COMPILERS = (SQLCompiler,
|
||||
SQLAggregateCompiler, SQLDateCompiler, SQLDateTimeCompiler,
|
||||
SQLInsertCompiler, SQLUpdateCompiler, SQLDeleteCompiler)
|
||||
WRITE_COMPILERS = (SQLInsertCompiler, SQLUpdateCompiler, SQLDeleteCompiler)
|
||||
READ_COMPILERS = [c for c in COMPILERS if c not in WRITE_COMPILERS]
|
||||
|
||||
|
||||
PATCHED = False
|
||||
MISS_VALUE = '[[The cache key was missed]]'
|
||||
|
||||
|
||||
def _get_tables_cache_keys(compiler):
|
||||
q = compiler.query
|
||||
# FIXME: `.extra` (and maybe more) are not in alias_map
|
||||
tables = q.alias_map.keys()
|
||||
|
||||
return ['%s_queries' % t for t in tables]
|
||||
|
||||
|
||||
def _update_tables_queries(compiler, cache_key):
|
||||
tables_cache_keys = _get_tables_cache_keys(compiler)
|
||||
tables_queries = cache.get_many(tables_cache_keys)
|
||||
for k in tables_cache_keys:
|
||||
queries = tables_queries.get(k, [])
|
||||
queries.append(cache_key)
|
||||
tables_queries[k] = queries
|
||||
cache.set_many(tables_queries)
|
||||
|
||||
|
||||
def _invalidate_tables(compiler):
|
||||
tables_cache_keys = _get_tables_cache_keys(compiler)
|
||||
tables_queries = cache.get_many(tables_cache_keys)
|
||||
queries = []
|
||||
for k in tables_cache_keys:
|
||||
queries.extend(tables_queries.get(k, []))
|
||||
cache.delete_many(queries)
|
||||
cache.delete_many(tables_cache_keys)
|
||||
|
||||
|
||||
def _monkey_patch_orm_read():
|
||||
def patch_execute_sql(method):
|
||||
def inner(compiler, *args, **kwargs):
|
||||
if isinstance(compiler, WRITE_COMPILERS):
|
||||
return method(compiler, *args, **kwargs)
|
||||
|
||||
try:
|
||||
cache_key = compiler.as_sql()
|
||||
except EmptyResultSet:
|
||||
return method(compiler, *args, **kwargs)
|
||||
|
||||
result = cache.get(cache_key, MISS_VALUE)
|
||||
|
||||
if result == MISS_VALUE:
|
||||
result = method(compiler, *args, **kwargs)
|
||||
if isinstance(result, Iterable) \
|
||||
and not isinstance(result, (tuple, list)):
|
||||
result = list(result)
|
||||
|
||||
_update_tables_queries(compiler, cache_key)
|
||||
|
||||
cache.set(cache_key, result)
|
||||
|
||||
return result
|
||||
|
||||
return inner
|
||||
|
||||
for compiler in READ_COMPILERS:
|
||||
compiler.execute_sql = patch_execute_sql(compiler.execute_sql)
|
||||
|
||||
|
||||
def _monkey_patch_orm_write():
|
||||
def patch_execute_sql(method):
|
||||
def inner(compiler, *args, **kwargs):
|
||||
_invalidate_tables(compiler)
|
||||
return method(compiler, *args, **kwargs)
|
||||
return inner
|
||||
|
||||
for compiler in WRITE_COMPILERS:
|
||||
compiler.execute_sql = patch_execute_sql(compiler.execute_sql)
|
||||
|
||||
|
||||
def monkey_patch_orm():
|
||||
global PATCHED
|
||||
_monkey_patch_orm_write()
|
||||
_monkey_patch_orm_read()
|
||||
PATCHED = True
|
||||
|
||||
|
||||
def is_patched():
|
||||
return PATCHED
|
Binary file not shown.
After Width: | Height: | Size: 115 KiB |
|
@ -0,0 +1 @@
|
|||
Django>=1.6
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
from setuptools import setup, find_packages
|
||||
from cachalot import version_string
|
||||
|
||||
|
||||
CURRENT_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
with open(os.path.join(CURRENT_PATH, 'requirements.txt')) as f:
|
||||
required = f.read().splitlines()
|
||||
|
||||
|
||||
setup(
|
||||
name='django-cachalot',
|
||||
version=version_string,
|
||||
author='Bertrand Bordage',
|
||||
author_email='bordage.bertrand@gmail.com',
|
||||
url='https://github.com/BertrandBordage/django-cachalot',
|
||||
description='Caches your Django ORM queries '
|
||||
'and automatically invalidates them.',
|
||||
long_description=open('README.rst').read(),
|
||||
classifiers=[
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Framework :: Django',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.2',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
],
|
||||
license='BSD',
|
||||
packages=find_packages(),
|
||||
install_requires=required,
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
)
|
Loading…
Reference in New Issue