KvsModel: Using KVS (like TokyoTyrant) as a Model

Django
2011-06-12 12:17 (13 years ago) ytyng
# -*- 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
Currently unrated

Comments

Archive

2024
2023
2022
2021
2020
2019
2018
2017
2016
2015
2014
2013
2012
2011