# -*- coding: utf-8 -*-
from livestreet.models.config import g
__all__ = (
"Property",
"KeyProperty",
"RelationProperty",
"IndexProperty",
"ModelMeta",
"Model",
)
class BaseProperty(object):
name = None
def __get__(self, model_instance, owner=None):
raise AttributeError, "Cann't get attribute."
def __set__(self, instance, value):
raise AttributeError, "Cann't set attribute."
def set_name(self, name):
self.name = name
class Property(BaseProperty):
def __init__(self, set_function, default=None, required=False):
self.name = None
self.set_function = set_function
self.default = default
self.required = required
def __get__(self, model_instance, owner=None):
return self.name in model_instance._data and model_instance._data[self.name] or self.get_default(model_instance)
def __set__(self, model_instance, value):
model_instance._data[self.name] = self.set_function(value)
def get_default(self, model_instance):
if self.required:
raise AttributeError, "Required attribute `%s`." % self.name
value = callable(self.default) and self.default() or self.default
model_instance._data[self.name] = value
return value
def __delete__(self, model_instance):
del model_instance._data[self.name]
class IndexProperty(BaseProperty):
def __init__(self, key_or_list, unique=False):
self.key_or_list = key_or_list
self.unique = unique
@property
def kwargs(self):
return {
'key_or_list': self.key_or_list,
'name': self.name,
'unique': self.unique,
}
class KeyProperty(Property):
def __init__(self, set_function):
super(KeyProperty, self).__init__(set_function, required=True)
class RelationProperty(Property):
def __init__(self, model=None, default=None, required=False):
model = model or 'any'
if model in ['self', 'any']:
pass
elif not issubclass(model, Model):
raise AttributeError, 'Must be a model subclass.'
super(RelationProperty, self).__init__(model, default, required)
def __get__(self, model_instance, owner=None):
return self.name in model_instance._rel and model_instance._rel[self.name] or self.get_relation(model_instance)
def get_relation(self, model_instance):
value = super(RelationProperty, self).__get__(model_instance)
if value:
value = g.config.backend.get_relation(model_instance.__class__, value)
model_instance._rel[self.name] = value
return value
def __set__(self, model_instance, value):
if not (value is None or isinstance(value, Model)):
raise AttributeError, 'Must be a Model instance.'
model_instance._rel[self.name] = value
if value:
value = g.config.backend.set_relation(value.__class__, value)
model_instance._data[self.name] = value
class ModelMeta(type):
MODELS = {}
def __new__(mcs, name, bases, attrs):
def get_properties(model):
fields = []
indexes = []
set_pk_name = False
for name in model.__dict__.keys():
property = model.__dict__[name]
if isinstance(property, BaseProperty):
if not property.name:
property.set_name(name)
if issubclass(model, Model) and isinstance(property, KeyProperty):
if set_pk_name:
raise ValueError, "More than one key property."
model.pk_name, set_pk_name = name, True
if isinstance(property, IndexProperty):
indexes.append(property.kwargs)
else:
fields.append(property)
for cls in model.mro():
if cls == model or cls.__name__ == 'object':
continue
_fields, _indexes = get_properties(cls)
fields.extend(_fields)
indexes.extend(_indexes)
return fields, indexes
model = super(ModelMeta, mcs).__new__(mcs, name, bases, attrs)
meta = model._meta and model._meta.copy() or {}
name = meta.get('name', model.__name__)
if name != 'Model':
if name in mcs.MODELS:
raise ValueError, "Model with name `%s` already registered." % name
mcs.MODELS[name] = model
meta['model'] = name
_fields, _indexes = get_properties(model)
meta['fields'] = _fields
meta['indexes'] = _indexes
model._meta = meta
return model
@classmethod
def factory(mcs, obj):
model = mcs.MODELS[obj['_model']]
return model.create(obj)
class Model(object):
pk_name = None
__metaclass__ = ModelMeta
__collection__ = None
_data = None
_meta = None
_new = True
def __init__(self, **initial):
self._rel = {}
self._data = {}
for k, v in initial.iteritems():
setattr(self, k, v)
@classmethod
def create(cls, data):
instance = cls()
instance._new, instance._data = False, data
return instance
@property
def id(self):
# TODO to backend
return self._data.get(self.pk_name, self._data.get('_id'))
@property
def key(self):
return self.backend.set_relation(self.__class__, self)
@property
def backend(self):
return g.config.backend
@property
def fields(self):
for field in self._meta['fields']:
yield field
def __repr__(self):
return '<Model instance %r>' % self._data
def __eq__(self, other):
return isinstance(other, Model) and self.id == other.id
def __ne__(self, other):
return not self == other
def get_backend_data(self):
data = {}
for field in self._meta['fields']:
data[field.name] = field.to_db(self)
return data
@classmethod
def get(cls, id=None, **kwargs):
for k in kwargs.keys():
if isinstance(kwargs[k], Model):
kwargs[k] = kwargs[k].key
return g.config.backend.get(cls, id, **kwargs)
def put(self):
if self._new:
for field in self._meta['fields']:
if not field.name in self._data:
field.__get__(self)
return self.backend.put(self)
def delete(self):
return self.backend.delete(self.__class__, self.id)
@classmethod
def filter(cls, **kwargs):
for k in kwargs.keys():
if isinstance(kwargs[k], Model):
kwargs[k] = kwargs[k].key
return g.config.backend.filter(cls, **kwargs)
def incr(self, field_name, value=1):
self.backend.incr(self, field_name, value)
class RelationModel(object):
def __init__(self, model, rel):
self.instance = None
self.model = model
self.rel = rel
def __getattribute__(self, item):
if self.instance is None:
self.instance = g.config.backend.get_relation(self.model, item)
return getattr(self.instance, item)