modified model metaclass to replace column definitions with properties that modify the columns value member instead of removing them until instantiation

This commit is contained in:
Blake Eggleston
2012-11-11 16:37:26 -08:00
parent 4764b492eb
commit 142ceb184c
3 changed files with 55 additions and 21 deletions

View File

@@ -21,6 +21,8 @@ class BaseColumn(object):
self.default = default
self.null = null
self.value = None
def validate(self, value):
"""
Returns a cleaned and validated value. Raises a ValidationError
@@ -40,6 +42,14 @@ class BaseColumn(object):
"""
return value
def to_database(self, value):
"""
Converts python value into database value
"""
if value is None and self.has_default:
return self.get_default()
return value
@property
def has_default(self):
return bool(self.default)
@@ -48,6 +58,33 @@ class BaseColumn(object):
def is_primary_key(self):
return self.primary_key
#methods for replacing column definitions with properties that interact
#with a column's value member
#this will allow putting logic behind value access (lazy loading, etc)
def _getval(self):
""" This columns value getter """
return self.value
def _setval(self, val):
""" This columns value setter """
self.value = val
def _delval(self):
""" This columns value deleter """
raise NotImplementedError
def get_property(self, allow_delete=True):
"""
Returns the property object that will set and get this
column's value and be assigned to this column's model attribute
"""
getval = lambda slf: self._getval()
setval = lambda slf, val: self._setval(val)
delval = lambda slf: self._delval()
if not allow_delete:
return property(getval, setval)
return property(getval, setval, delval)
def get_default(self):
if self.has_default:
if callable(self.default):
@@ -55,14 +92,6 @@ class BaseColumn(object):
else:
return self.default
def to_database(self, value):
"""
Converts python value into database value
"""
if value is None and self.has_default:
return self.get_default()
return value
def get_column_def(self):
"""
Returns a column definition for CQL table definition

View File

@@ -32,7 +32,7 @@ class BaseModel(object):
@property
def pk(self):
""" Returns the object's primary key, regardless of it's name """
""" Returns the object's primary key """
return getattr(self, self._pk_name)
#dynamic column methods
@@ -78,26 +78,31 @@ class ModelMetaClass(type):
#and set default column names
_columns = {}
pk_name = None
def _transform_column(col_name, col_obj):
_columns[col_name] = col_obj
col_obj.set_db_name(col_name)
allow_delete = not col_obj.primary_key
attrs[col_name] = col_obj.get_property(allow_delete=allow_delete)
#transform column definitions
for k,v in attrs.items():
if isinstance(v, columns.BaseColumn):
if v.is_primary_key:
if pk_name:
raise ModelException("More than one primary key defined for {}".format(name))
pk_name = k
_columns[k] = attrs.pop(k)
_columns[k].set_db_name(k)
_transform_column(k,v)
#set primary key if it's not already defined
if not pk_name:
_columns['id'] = columns.UUID(primary_key=True)
_columns['id'].set_db_name('id')
pk_name = 'id'
k,v = 'id', columns.UUID(primary_key=True)
_transform_column(k,v)
pk_name = k
#setup pk shortcut
#setup primary key shortcut
if pk_name != 'pk':
pk_get = lambda self: getattr(self, pk_name)
pk_set = lambda self, val: setattr(self, pk_name, val)
attrs['pk'] = property(pk_get, pk_set)
attrs['pk'] = _columns[pk_name].get_property(allow_delete=False)
#check for duplicate column names
col_names = set()
@@ -107,7 +112,7 @@ class ModelMetaClass(type):
col_names.add(v.db_field)
#get column family name
cf_name = attrs.pop('db_name', None) or name
cf_name = attrs.pop('db_name', name)
#create db_name -> model name map for loading
db_map = {}

View File

@@ -19,8 +19,8 @@ class TestModelClassFunction(BaseCassEngTestCase):
text = columns.Text()
self.assertHasAttr(TestModel, '_columns')
self.assertNotHasAttr(TestModel, 'id')
self.assertNotHasAttr(TestModel, 'text')
self.assertHasAttr(TestModel, 'id')
self.assertHasAttr(TestModel, 'text')
inst = TestModel()
self.assertHasAttr(inst, 'id')