# -*- coding: utf-8 -*- """ A key/value store based on a relational database table. Usage:: # Store a value, retrieve, show and delete a value for a key. KeyValue.set(u'foo', u'bar') print KeyValue.get(u'foo') KeyValue.delete(u'foo') # Increment a counter (pre-initialized at zero) and show the result. hits = KeyValue.incr(u'hits') print 'Hits:', hits # Use optional JSON serialization (`None`, boolean, number, string, # and list types only). KeyValue.set(u'fibonacci', [0, 1, 1, 2, 3, 5, 8], True) print ' -> '.join(KeyValue.get(u'fibonacci', True)) """ import simplejson as json from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import scoped_session, sessionmaker from sqlalchemy.schema import Column, MetaData from sqlalchemy.types import Unicode, UnicodeText # Prepare metadata, session, and Base class with query attribute. metadata = MetaData() dbsession = scoped_session(sessionmaker()) Base = declarative_base(metadata=metadata) Base.q = dbsession.query_property() class KeyValue(Base): """A key/value record.""" __tablename__ = 'key_values' key = Column(Unicode, primary_key=True) value = Column(UnicodeText) @classmethod def get(cls, key, serialized=False): """Retrieve the value stored at the given key.""" kv = cls.q.get(key) if kv is None: # Key does not exist. return if serialized: return json.loads(kv.value) return kv.value @classmethod def set(cls, key, value, serialized=False): """Store the given value at the given key. If the key exists, the value is overwritten, otherwise the key is created and the value assigned to it. """ if serialized: value = unicode(json.dumps(value)) kv = cls.q.get(key) if kv is None: dbsession.add(KeyValue(key=key, value=value)) else: kv.value = value dbsession.commit() @classmethod def incr(cls, key, amount=1): """Increment an integer value by the given amount, save and return the resulting number. """ count = cls.get(key) if count is None: count = 0 else: try: count = int(count) except ValueError: raise ValueError('Key exists, but has a non-integer value.') count += amount cls.set(key, unicode(count)) return count @classmethod def delete(cls, key): """Delete the key and its value.""" kv = cls.q.get(key) if kv is not None: dbsession.delete(kv) dbsession.commit()