fixed #67 (validation on keyname typos) #66 (removed defaults on columns) and #57 and removed autoid
This commit is contained in:
		| @@ -1,5 +1,9 @@ | ||||
| CHANGELOG | ||||
|  | ||||
| 0.4.0 | ||||
| * removed default values from all column types | ||||
| * explicit primary key is required (automatic id removed) | ||||
|  | ||||
| 0.3.3 | ||||
| * added abstract base class models | ||||
|  | ||||
|   | ||||
| @@ -236,9 +236,6 @@ class Integer(Column): | ||||
| class DateTime(Column): | ||||
|     db_type = 'timestamp' | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         super(DateTime, self).__init__(**kwargs) | ||||
|  | ||||
|     def to_python(self, value): | ||||
|         if isinstance(value, datetime): | ||||
|             return value | ||||
| @@ -265,8 +262,6 @@ class DateTime(Column): | ||||
| class Date(Column): | ||||
|     db_type = 'timestamp' | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         super(Date, self).__init__(**kwargs) | ||||
|  | ||||
|     def to_python(self, value): | ||||
|         if isinstance(value, datetime): | ||||
| @@ -294,9 +289,6 @@ class UUID(Column): | ||||
|  | ||||
|     re_uuid = re.compile(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') | ||||
|  | ||||
|     def __init__(self, default=lambda:uuid4(), **kwargs): | ||||
|         super(UUID, self).__init__(default=default, **kwargs) | ||||
|  | ||||
|     def validate(self, value): | ||||
|         val = super(UUID, self).validate(value) | ||||
|         if val is None: return | ||||
| @@ -319,10 +311,6 @@ class TimeUUID(UUID): | ||||
|  | ||||
|     db_type = 'timeuuid' | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         kwargs.setdefault('default', lambda: uuid1()) | ||||
|         super(TimeUUID, self).__init__(**kwargs) | ||||
|  | ||||
| class Boolean(Column): | ||||
|     db_type = 'boolean' | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ from collections import OrderedDict | ||||
| import re | ||||
|  | ||||
| from cqlengine import columns | ||||
| from cqlengine.exceptions import ModelException, CQLEngineException | ||||
| from cqlengine.exceptions import ModelException, CQLEngineException, ValidationError | ||||
| from cqlengine.query import QuerySet, DMLQuery | ||||
| from cqlengine.query import DoesNotExist as _DoesNotExist | ||||
| from cqlengine.query import MultipleObjectsReturned as _MultipleObjectsReturned | ||||
| @@ -50,7 +50,7 @@ class BaseModel(object): | ||||
|     """ | ||||
|     The base model class, don't inherit from this, inherit from Model, defined below | ||||
|     """ | ||||
|      | ||||
|  | ||||
|     class DoesNotExist(_DoesNotExist): pass | ||||
|     class MultipleObjectsReturned(_MultipleObjectsReturned): pass | ||||
|  | ||||
| @@ -60,12 +60,17 @@ class BaseModel(object): | ||||
|     #however, you can also define them manually here | ||||
|     table_name = None | ||||
|  | ||||
|     #the keyspace for this model  | ||||
|     #the keyspace for this model | ||||
|     keyspace = None | ||||
|     read_repair_chance = 0.1 | ||||
|  | ||||
|     def __init__(self, **values): | ||||
|         self._values = {} | ||||
|  | ||||
|         extra_columns = set(values.keys()) - set(self._columns.keys()) | ||||
|         if extra_columns: | ||||
|             raise ValidationError("Incorrect columns passed: {}".format(extra_columns)) | ||||
|  | ||||
|         for name, column in self._columns.items(): | ||||
|             value =  values.get(name, None) | ||||
|             if value is not None: value = column.to_python(value) | ||||
| @@ -111,11 +116,11 @@ class BaseModel(object): | ||||
|         else: | ||||
|             camelcase = re.compile(r'([a-z])([A-Z])') | ||||
|             ccase = lambda s: camelcase.sub(lambda v: '{}_{}'.format(v.group(1), v.group(2).lower()), s) | ||||
|      | ||||
|  | ||||
|             module = cls.__module__.split('.') | ||||
|             if module: | ||||
|                 cf_name = ccase(module[-1]) + '_' | ||||
|      | ||||
|  | ||||
|             cf_name += ccase(cls.__name__) | ||||
|             #trim to less than 48 characters or cassandra will complain | ||||
|             cf_name = cf_name[-48:] | ||||
| @@ -140,15 +145,15 @@ class BaseModel(object): | ||||
|     @classmethod | ||||
|     def create(cls, **kwargs): | ||||
|         return cls.objects.create(**kwargs) | ||||
|      | ||||
|  | ||||
|     @classmethod | ||||
|     def all(cls): | ||||
|         return cls.objects.all() | ||||
|      | ||||
|  | ||||
|     @classmethod | ||||
|     def filter(cls, **kwargs): | ||||
|         return cls.objects.filter(**kwargs) | ||||
|      | ||||
|  | ||||
|     @classmethod | ||||
|     def get(cls, **kwargs): | ||||
|         return cls.objects.get(**kwargs) | ||||
| @@ -226,8 +231,7 @@ class ModelMetaClass(type): | ||||
|  | ||||
|         #prepend primary key if one hasn't been defined | ||||
|         if not is_abstract and not any([v.primary_key for k,v in column_definitions]): | ||||
|             k,v = 'id', columns.UUID(primary_key=True) | ||||
|             column_definitions = [(k,v)] + column_definitions | ||||
|             raise ModelDefinitionException("At least 1 primary key is required.") | ||||
|  | ||||
|         has_partition_keys = any(v.partition_key for (k, v) in column_definitions) | ||||
|  | ||||
|   | ||||
| @@ -3,6 +3,7 @@ from datetime import datetime, timedelta | ||||
| from datetime import date | ||||
| from datetime import tzinfo | ||||
| from decimal import Decimal as D | ||||
| from uuid import uuid4, uuid1 | ||||
| from cqlengine import ValidationError | ||||
|  | ||||
| from cqlengine.tests.base import BaseCassEngTestCase | ||||
| @@ -119,7 +120,7 @@ class TestDecimal(BaseCassEngTestCase): | ||||
| class TestTimeUUID(BaseCassEngTestCase): | ||||
|     class TimeUUIDTest(Model): | ||||
|         test_id = Integer(primary_key=True) | ||||
|         timeuuid = TimeUUID() | ||||
|         timeuuid = TimeUUID(default=uuid1()) | ||||
|  | ||||
|     @classmethod | ||||
|     def setUpClass(cls): | ||||
| @@ -132,6 +133,10 @@ class TestTimeUUID(BaseCassEngTestCase): | ||||
|         delete_table(cls.TimeUUIDTest) | ||||
|  | ||||
|     def test_timeuuid_io(self): | ||||
|         """ | ||||
|         ensures that | ||||
|         :return: | ||||
|         """ | ||||
|         t0 = self.TimeUUIDTest.create(test_id=0) | ||||
|         t1 = self.TimeUUIDTest.get(test_id=0) | ||||
|  | ||||
| @@ -139,8 +144,8 @@ class TestTimeUUID(BaseCassEngTestCase): | ||||
|  | ||||
| class TestInteger(BaseCassEngTestCase): | ||||
|     class IntegerTest(Model): | ||||
|         test_id = UUID(primary_key=True) | ||||
|         value   = Integer(default=0) | ||||
|         test_id = UUID(primary_key=True, default=lambda:uuid4()) | ||||
|         value   = Integer(default=0, required=True) | ||||
|  | ||||
|     def test_default_zero_fields_validate(self): | ||||
|         """ Tests that integer columns with a default value of 0 validate """ | ||||
| @@ -190,7 +195,13 @@ class TestText(BaseCassEngTestCase): | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class TestExtraFieldsRaiseException(BaseCassEngTestCase): | ||||
|     class TestModel(Model): | ||||
|         id = UUID(primary_key=True, default=uuid4) | ||||
|  | ||||
|     def test_extra_field(self): | ||||
|         with self.assertRaises(ValidationError): | ||||
|             self.TestModel.create(bacon=5000) | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| from uuid import uuid4 | ||||
| from cqlengine.query import QueryException | ||||
| from cqlengine.tests.base import BaseCassEngTestCase | ||||
|  | ||||
| from cqlengine.exceptions import ModelException, CQLEngineException | ||||
| from cqlengine.models import Model | ||||
| from cqlengine.models import Model, ModelDefinitionException | ||||
| from cqlengine import columns | ||||
| import cqlengine | ||||
|  | ||||
| @@ -18,6 +19,7 @@ class TestModelClassFunction(BaseCassEngTestCase): | ||||
|         """ | ||||
|  | ||||
|         class TestModel(Model): | ||||
|             id  = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|             text = columns.Text() | ||||
|  | ||||
|         #check class attibutes | ||||
| @@ -38,6 +40,7 @@ class TestModelClassFunction(BaseCassEngTestCase): | ||||
|         -the db_map allows columns | ||||
|         """ | ||||
|         class WildDBNames(Model): | ||||
|             id  = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|             content = columns.Text(db_field='words_and_whatnot') | ||||
|             numbers = columns.Integer(db_field='integers_etc') | ||||
|  | ||||
| @@ -61,17 +64,26 @@ class TestModelClassFunction(BaseCassEngTestCase): | ||||
|         """ | ||||
|  | ||||
|         class Stuff(Model): | ||||
|             id  = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|             words = columns.Text() | ||||
|             content = columns.Text() | ||||
|             numbers = columns.Integer() | ||||
|  | ||||
|         self.assertEquals(Stuff._columns.keys(), ['id', 'words', 'content', 'numbers']) | ||||
|  | ||||
|     def test_exception_raised_when_creating_class_without_pk(self): | ||||
|         with self.assertRaises(ModelDefinitionException): | ||||
|             class TestModel(Model): | ||||
|                 count   = columns.Integer() | ||||
|                 text    = columns.Text(required=False) | ||||
|  | ||||
|  | ||||
|     def test_value_managers_are_keeping_model_instances_isolated(self): | ||||
|         """ | ||||
|         Tests that instance value managers are isolated from other instances | ||||
|         """ | ||||
|         class Stuff(Model): | ||||
|             id  = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|             num = columns.Integer() | ||||
|  | ||||
|         inst1 = Stuff(num=5) | ||||
| @@ -86,6 +98,7 @@ class TestModelClassFunction(BaseCassEngTestCase): | ||||
|         Tests that fields defined on the super class are inherited properly | ||||
|         """ | ||||
|         class TestModel(Model): | ||||
|             id  = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|             text = columns.Text() | ||||
|  | ||||
|         class InheritedModel(TestModel): | ||||
| @@ -124,6 +137,7 @@ class TestModelClassFunction(BaseCassEngTestCase): | ||||
|         Test compound partition key definition | ||||
|         """ | ||||
|         class ModelWithPartitionKeys(cqlengine.Model): | ||||
|             id = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|             c1 = cqlengine.Text(primary_key=True) | ||||
|             p1 = cqlengine.Text(partition_key=True) | ||||
|             p2 = cqlengine.Text(partition_key=True) | ||||
| @@ -144,6 +158,7 @@ class TestModelClassFunction(BaseCassEngTestCase): | ||||
|     def test_del_attribute_is_assigned_properly(self): | ||||
|         """ Tests that columns that can be deleted have the del attribute """ | ||||
|         class DelModel(Model): | ||||
|             id  = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|             key = columns.Integer(primary_key=True) | ||||
|             data = columns.Integer(required=False) | ||||
|  | ||||
| @@ -156,9 +171,10 @@ class TestModelClassFunction(BaseCassEngTestCase): | ||||
|         """ Tests that DoesNotExist exceptions are not the same exception between models """ | ||||
|  | ||||
|         class Model1(Model): | ||||
|             pass | ||||
|             id  = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|  | ||||
|         class Model2(Model): | ||||
|             pass | ||||
|             id  = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|  | ||||
|         try: | ||||
|             raise Model1.DoesNotExist | ||||
| @@ -171,7 +187,8 @@ class TestModelClassFunction(BaseCassEngTestCase): | ||||
|     def test_does_not_exist_inherits_from_superclass(self): | ||||
|         """ Tests that a DoesNotExist exception can be caught by it's parent class DoesNotExist """ | ||||
|         class Model1(Model): | ||||
|             pass | ||||
|             id  = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|  | ||||
|         class Model2(Model1): | ||||
|             pass | ||||
|  | ||||
| @@ -184,14 +201,14 @@ class TestModelClassFunction(BaseCassEngTestCase): | ||||
|             assert False, "Model2 exception should not be caught by Model1" | ||||
|  | ||||
| class TestManualTableNaming(BaseCassEngTestCase): | ||||
|      | ||||
|  | ||||
|     class RenamedTest(cqlengine.Model): | ||||
|         keyspace = 'whatever' | ||||
|         table_name = 'manual_name' | ||||
|          | ||||
|  | ||||
|         id = cqlengine.UUID(primary_key=True) | ||||
|         data = cqlengine.Text() | ||||
|          | ||||
|  | ||||
|     def test_proper_table_naming(self): | ||||
|         assert self.RenamedTest.column_family_name(include_keyspace=False) == 'manual_name' | ||||
|         assert self.RenamedTest.column_family_name(include_keyspace=True) == 'whatever.manual_name' | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| from unittest import skip | ||||
| from uuid import uuid4 | ||||
| from cqlengine.tests.base import BaseCassEngTestCase | ||||
|  | ||||
| from cqlengine.management import create_table | ||||
| @@ -7,6 +8,7 @@ from cqlengine.models import Model | ||||
| from cqlengine import columns | ||||
|  | ||||
| class TestModel(Model): | ||||
|     id      = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|     count   = columns.Integer() | ||||
|     text    = columns.Text(required=False) | ||||
|  | ||||
|   | ||||
| @@ -8,10 +8,18 @@ from cqlengine.models import Model | ||||
| from cqlengine import columns | ||||
|  | ||||
| class TestModel(Model): | ||||
|     id      = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|     count   = columns.Integer() | ||||
|     text    = columns.Text(required=False) | ||||
|     a_bool  = columns.Boolean(default=False) | ||||
|  | ||||
| class TestModel(Model): | ||||
|     id      = columns.UUID(primary_key=True, default=lambda:uuid4()) | ||||
|     count   = columns.Integer() | ||||
|     text    = columns.Text(required=False) | ||||
|     a_bool  = columns.Boolean(default=False) | ||||
|  | ||||
|  | ||||
| class TestModelIO(BaseCassEngTestCase): | ||||
|  | ||||
|     @classmethod | ||||
| @@ -34,6 +42,8 @@ class TestModelIO(BaseCassEngTestCase): | ||||
|         for cname in tm._columns.keys(): | ||||
|             self.assertEquals(getattr(tm, cname), getattr(tm2, cname)) | ||||
|  | ||||
|  | ||||
|  | ||||
|     def test_model_updating_works_properly(self): | ||||
|         """ | ||||
|         Tests that subsequent saves after initial model creation work | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jon Haddad
					Jon Haddad