KvsModel: Using KVS (like TokyoTyrant) as a Model
Django
2011-06-12 03:17 (14 years ago)

# -*- coding: utf-8 -*-
"""
Using KVS (like TokyoTyrant) as a model
This time, we will handle Django's cache
Although it is called a model, it does not provide relations or searches,
but allows field definitions like models.Model.
(In essence, it automatically assigns the key names of the dictionary (storage) to the variable names.)
class Soldier(KvsModel):
hp = KvsModel.Field(default=0)
mp = KvsModel.Field(default=0)
cain = Soldier('Cain')
cain.hp = 100
cain.mp = 50
cain.save()
abel = Soldier('Abel')
abel.hp = 50
abel.mp = 100
abel.save()
cain = Soldier('Cain')
cain.hp -= 80
cain.save()
abel = Soldier('Abel')
abel.mp -= 20
abel.save()
cain = Soldier('Cain')
cain.hp = min(cain.hp + 50, 100)
cain.save()
cain = Soldier('Cain')
cain.hp
Out: 70
cain.dump()
Out: "[Soldier] dump: kvs_key=Soldier::Cain, storage={'hp': 70, 'mp': 50}"
abel.dump()
Out: "[Soldier] dump: kvs_key=Soldier::Abel, storage={'hp': 50, 'mp': 80}"
"""
import msgpack #pip install msgpack-python
from django.core.cache import cache as kvs_backend
class KvsModel(object):
"""
Base class to use TokyoTyrant etc. as a dictionary
Please use by inheriting
"""
packer = msgpack.Packer()
def __new__(cls, *args, **kwargs):
for k, v in vars(cls).items():
if isinstance(v, KvsModel.Field):
v.set_field_name(k)
return object.__new__(cls)
def __init__(self, key_object):
"""
@param key_object An instance of a Django model, etc.
When overriding and there is no need to call super's __init__,
directly call self.after_init(key_part)
"""
self.key_object = key_object
key_part = getattr(key_object, 'pk', str(key_object))
self.after_init(key_part)
def after_init(self, key_part):
"""
The latter half of the initialization process
@param (str)key_part A unique string that forms the basis of the key generation
"""
self.kvs_key = '%s::%s' % (self.__class__.__name__, key_part)
bulk_storage = kvs_backend.get(self.kvs_key, None)
if bulk_storage:
self.storage = msgpack.unpackb(bulk_storage)
else:
self.storage = {}
def get(self, k, default=None):
return self.storage.get(k, default)
def set(self, k, value):
self.storage[k] = value
def save(self):
"""
Save storage
"""
kvs_backend.set(self.kvs_key, self.packer.pack(self.storage))
def delete(self):
"""
Delete storage
"""
kvs_backend.delete(self.kvs_key)
self.storage = {}
def dump(self):
return "[%s] dump: kvs_key=%s, storage=%r" % (self.__class__.__name__, self.kvs_key, self.storage)
class Field(object):
"""
Field usable in KvsModel
Can be used like score = KvsModel.Field(default=0).
"""
def __init__(self, key_name=None, default=None):
self._key_name = key_name
self.default = default
def __get__(self, inst, type=None):
assert isinstance(inst, KvsModel), '%s use only KvsModel class.' % self.__class__.__name__
return inst.get(self.key_name, self.default)
def __set__(self, inst, value):
assert isinstance(inst, KvsModel), '%s use only KvsModel class.' % self.__class__.__name__
inst.set(self.key_name, value)
def set_field_name(self, field_name):
# Called from TokyoTyrantStorage.__new__
self.field_name = field_name
@property
def key_name(self):
return self._key_name or self.field_name
Please rate this article
Currently unrated
The author runs the application development company Cyberneura.
We look forward to discussing your development needs.
We look forward to discussing your development needs.