Added automatic expiration of proxy dicts, updated sqlalchemy in requirements

This commit is contained in:
Konsta Vesterinen
2013-05-29 13:10:42 +03:00
parent 92880f9105
commit 2658d8b436
5 changed files with 63 additions and 23 deletions

View File

@@ -4,6 +4,13 @@ Changelog
Here you can see the full list of changes between each SQLAlchemy-Utils release. Here you can see the full list of changes between each SQLAlchemy-Utils release.
0.12.2 (2013-05-29)
^^^^^^^^^^^^^^^^^^^
- Added automatic expiration of proxy dicts
0.12.1 (2013-05-18) 0.12.1 (2013-05-18)
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^

View File

@@ -24,7 +24,7 @@ class PyTest(Command):
setup( setup(
name='SQLAlchemy-Utils', name='SQLAlchemy-Utils',
version='0.12.1', version='0.12.2',
url='https://github.com/kvesteri/sqlalchemy-utils', url='https://github.com/kvesteri/sqlalchemy-utils',
license='BSD', license='BSD',
author='Konsta Vesterinen', author='Konsta Vesterinen',
@@ -38,7 +38,7 @@ setup(
include_package_data=True, include_package_data=True,
platforms='any', platforms='any',
install_requires=[ install_requires=[
'SQLAlchemy>=0.7.8', 'SQLAlchemy>=0.8.0',
'phonenumbers>=5.4b1', 'phonenumbers>=5.4b1',
'colour==0.0.2' 'colour==0.0.2'
], ],

View File

@@ -1,7 +1,7 @@
from .functions import sort_query, defer_except, escape_like from .functions import sort_query, defer_except, escape_like, primary_keys
from .listeners import coercion_listener from .listeners import coercion_listener
from .merge import merge, Merger from .merge import merge, Merger
from .proxy_dict import ProxyDict from .proxy_dict import ProxyDict, proxy_dict
from .types import ( from .types import (
ColorType, ColorType,
EmailType, EmailType,
@@ -25,6 +25,8 @@ __all__ = (
escape_like, escape_like,
instrumented_list, instrumented_list,
merge, merge,
primary_keys,
proxy_dict,
ColorType, ColorType,
EmailType, EmailType,
InstrumentedList, InstrumentedList,

View File

@@ -2,11 +2,11 @@ import sqlalchemy as sa
class ProxyDict(object): class ProxyDict(object):
def __init__(self, parent, collection_name, child_class, key_name): def __init__(self, parent, collection_name, mapping_attr):
self.parent = parent self.parent = parent
self.collection_name = collection_name self.collection_name = collection_name
self.child_class = child_class self.child_class = mapping_attr.class_
self.key_name = key_name self.key_name = mapping_attr.key
self.cache = {} self.cache = {}
@property @property
@@ -26,20 +26,22 @@ class ProxyDict(object):
def fetch(self, key): def fetch(self, key):
return self.collection.filter_by(**{self.key_name: key}).first() return self.collection.filter_by(**{self.key_name: key}).first()
def create_new_instance(self, key):
value = self.child_class(**{self.key_name: key})
self.collection.append(value)
return value
def __getitem__(self, key): def __getitem__(self, key):
if key in self.cache: if key in self.cache:
return self.cache[key] return self.cache[key]
session = sa.orm.object_session(self.parent) session = sa.orm.object_session(self.parent)
if not session or not sa.orm.util.has_identity(self.parent): if not session or not sa.orm.util.has_identity(self.parent):
value = self.child_class(**{self.key_name: key}) value = self.create_new_instance(key)
self.collection.append(value)
else: else:
value = self.fetch(key) value = self.fetch(key)
if not value: if not value:
value = self.child_class(**{self.key_name: key}) value = self.create_new_instance(key)
self.collection.append(value)
self.cache[key] = value self.cache[key] = value
return value return value
@@ -51,3 +53,24 @@ class ProxyDict(object):
pass pass
self.collection.append(value) self.collection.append(value)
self.cache[key] = value self.cache[key] = value
def proxy_dict(parent, collection_name, mapping_attr):
try:
parent._proxy_dicts
except AttributeError:
def expire_proxy_dicts(target, context):
target._proxy_dicts = {}
sa.event.listen(parent.__class__, 'expire', expire_proxy_dicts)
parent._proxy_dicts = {}
try:
return parent._proxy_dicts[collection_name]
except KeyError:
parent._proxy_dicts[collection_name] = ProxyDict(
parent,
collection_name,
mapping_attr
)
return parent._proxy_dicts[collection_name]

View File

@@ -1,6 +1,6 @@
from flexmock import flexmock from flexmock import flexmock
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy_utils import ProxyDict from sqlalchemy_utils import ProxyDict, proxy_dict
from tests import TestCase from tests import TestCase
@@ -21,16 +21,11 @@ class TestProxyDict(TestCase):
@property @property
def translations(self): def translations(self):
try: return proxy_dict(
return self.proxied_translations
except AttributeError:
self.proxied_translations = ProxyDict(
self, self,
'_translations', '_translations',
ArticleTranslation, ArticleTranslation.locale
'locale'
) )
return self.proxied_translations
class ArticleTranslation(self.Base): class ArticleTranslation(self.Base):
__tablename__ = 'article_translation' __tablename__ = 'article_translation'
@@ -83,3 +78,16 @@ class TestProxyDict(TestCase):
name=u'something' name=u'something'
) )
article.translations['en'] article.translations['en']
def test_committing_session_empties_proxy_dict_cache(self):
article = self.Article()
(
flexmock(ProxyDict)
.should_receive('fetch')
.twice()
)
self.session.add(article)
self.session.commit()
article.translations['en']
self.session.commit()
article.translations['en']