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:
@@ -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
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user