diff --git a/cassandraengine/columns.py b/cassandraengine/columns.py index 67f697ea..20f54ba8 100644 --- a/cassandraengine/columns.py +++ b/cassandraengine/columns.py @@ -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 diff --git a/cassandraengine/models.py b/cassandraengine/models.py index 040682d4..3d1388f9 100644 --- a/cassandraengine/models.py +++ b/cassandraengine/models.py @@ -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 = {} diff --git a/cassandraengine/tests/model/test_class_construction.py b/cassandraengine/tests/model/test_class_construction.py index cc24a8c0..5e3dbe19 100644 --- a/cassandraengine/tests/model/test_class_construction.py +++ b/cassandraengine/tests/model/test_class_construction.py @@ -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')