# -*- coding: utf-8 -*- """ KVS(TokyoTyrantなど)をモデルっぽく使う 今回は Django のキャッシュを扱う モデルといってもリレーションや検索などを提供するわけではなく フィールド定義がmodels.Modelみたいにできる。 (要は、中でもってるディクショナリ(storage)のキー名を変数名から自動的につけてくれる) 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): """ TokyoTyrantなどをディクショナリとして使う基底クラス 継承して使ってください """ packer = msgpack.Packer() def __new__(cls, *args, **kwargs): for k, v in vars(cls).iteritems(): if isinstance(v, KvsModel.Field): v.set_field_name(k) return object.__new__(cls) def __init__(self, key_object): """ @param key_object djangoのモデルインスタンスなど。 オーバーライドして、superの__init__を呼ぶ必要が無い時は そのまま 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): """ 初期化処理の後半 @param (str)key_part キー生成の元となるユニークな文字列 """ 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): """ storageを保存 """ kvs_backend.set(self.kvs_key, self.packer.pack(self.storage)) def delete(self): """ 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): """ KvsModel に使えるフィールド 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): #TokyoTyrantStorage.__new__ から呼ばれる self.field_name = field_name @property def key_name(self): return self._key_name or self.field_name
コメント