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