Merge pull request #491 from datastax/py337

PYTHON-337: case sensitive support for column family name
This commit is contained in:
Adam Holmberg
2016-02-29 14:25:06 -06:00
6 changed files with 105 additions and 3 deletions

View File

@@ -194,7 +194,7 @@ def sync_table(model):
if table.indexes.get(index_name):
continue
qs = ['CREATE INDEX {0}'.format(index_name)]
qs = ['CREATE INDEX {0}'.format(metadata.protect_name(index_name))]
qs += ['ON {0}'.format(cf_name)]
qs += ['("{0}")'.format(column.db_field_name)]
qs = ' '.join(qs)

View File

@@ -15,7 +15,7 @@
import logging
import re
import six
import warnings
from warnings import warn
from cassandra.cqlengine import CQLEngineException, ValidationError
from cassandra.cqlengine import columns
@@ -327,6 +327,8 @@ class BaseModel(object):
__table_name__ = None
__table_name_case_sensitive__ = False
__keyspace__ = None
__discriminator_value__ = None
@@ -520,7 +522,14 @@ class BaseModel(object):
def _raw_column_family_name(cls):
if not cls._table_name:
if cls.__table_name__:
cls._table_name = cls.__table_name__.lower()
if cls.__table_name_case_sensitive__:
cls._table_name = cls.__table_name__
else:
table_name = cls.__table_name__.lower()
if cls.__table_name__ != table_name:
warn(("Model __table_name__ will be case sensitive by default in 4.0. "
"You should fix the __table_name__ value of the '{0}' model.").format(cls.__name__))
cls._table_name = table_name
else:
if cls._is_polymorphic and not cls._is_polymorphic_base:
cls._table_name = cls._polymorphic_base._raw_column_family_name()
@@ -957,6 +966,11 @@ class Model(BaseModel):
*Optional.* Sets the name of the CQL table for this model. If left blank, the table name will be the name of the model, with it's module name as it's prefix. Manually defined table names are not inherited.
"""
__table_name_case_sensitive__ = False
"""
*Optional.* By default, __table_name__ is case insensitive. Set this to True if you want to preserve the case sensitivity.
"""
__keyspace__ = None
"""
Sets the name of the keyspace used by this model.

View File

@@ -28,6 +28,8 @@ Model
.. autoattribute:: __table_name__
.. autoattribute:: __table_name_case_sensitive__
.. autoattribute:: __keyspace__
.. _ttl-change:

View File

@@ -235,6 +235,49 @@ class SyncTableTests(BaseCassEngTestCase):
self.assertIn('SizeTieredCompactionStrategy', table_meta.as_cql_query())
class IndexModel(Model):
__table_name__ = 'index_model'
first_key = columns.UUID(primary_key=True)
second_key = columns.Text(index=True)
class IndexCaseSensitiveModel(Model):
__table_name__ = 'IndexModel'
__table_name_case_sensitive__ = True
first_key = columns.UUID(primary_key=True)
second_key = columns.Text(index=True)
class IndexTests(BaseCassEngTestCase):
def setUp(self):
drop_table(IndexModel)
def test_sync_index(self):
sync_table(IndexModel)
table_meta = management._get_table_metadata(IndexModel)
self.assertIn("index_index_model_second_key", table_meta.indexes)
# index already exists
sync_table(IndexModel)
table_meta = management._get_table_metadata(IndexModel)
self.assertIn("index_index_model_second_key", table_meta.indexes)
def test_sync_index_case_sensitive(self):
sync_table(IndexCaseSensitiveModel)
table_meta = management._get_table_metadata(IndexCaseSensitiveModel)
self.assertIn("index_IndexModel_second_key", table_meta.indexes)
# index already exists
sync_table(IndexCaseSensitiveModel)
table_meta = management._get_table_metadata(IndexCaseSensitiveModel)
self.assertIn("index_IndexModel_second_key", table_meta.indexes)
class NonModelFailureTest(BaseCassEngTestCase):
class FakeModel(object):
pass

View File

@@ -216,6 +216,7 @@ class TestModelClassFunction(BaseCassEngTestCase):
self.assertEqual(len(warn), 0)
class TestManualTableNaming(BaseCassEngTestCase):
class RenamedTest(Model):
@@ -229,6 +230,31 @@ class TestManualTableNaming(BaseCassEngTestCase):
assert self.RenamedTest.column_family_name(include_keyspace=False) == 'manual_name'
assert self.RenamedTest.column_family_name(include_keyspace=True) == 'whatever.manual_name'
class TestManualTableNamingCaseSensitive(BaseCassEngTestCase):
class RenamedCaseInsensitiveTest(Model):
__keyspace__ = 'whatever'
__table_name__ = 'Manual_Name'
id = columns.UUID(primary_key=True)
class RenamedCaseSensitiveTest(Model):
__keyspace__ = 'whatever'
__table_name__ = 'Manual_Name'
__table_name_case_sensitive__ = True
id = columns.UUID(primary_key=True)
def test_proper_table_naming_case_insensitive(self):
self.assertEqual(self.RenamedCaseInsensitiveTest.column_family_name(include_keyspace=False), 'manual_name')
self.assertEqual(self.RenamedCaseInsensitiveTest.column_family_name(include_keyspace=True), 'whatever.manual_name')
def test_proper_table_naming_case_sensitive(self):
self.assertEqual(self.RenamedCaseSensitiveTest.column_family_name(include_keyspace=False), '"Manual_Name"')
self.assertEqual(self.RenamedCaseSensitiveTest.column_family_name(include_keyspace=True), 'whatever."Manual_Name"')
class AbstractModel(Model):
__abstract__ = True

View File

@@ -126,6 +126,23 @@ class TestModel(unittest.TestCase):
# .. but we can still get the bare CF name
self.assertEqual(TestModel.column_family_name(include_keyspace=False), "test_model")
def test_column_family_case_sensitive(self):
class TestModel(Model):
__table_name__ = 'TestModel'
__table_name_case_sensitive__ = True
k = columns.Integer(primary_key=True)
self.assertEqual(TestModel.column_family_name(), '%s."TestModel"' % (models.DEFAULT_KEYSPACE,))
TestModel.__keyspace__ = "my_test_keyspace"
self.assertEqual(TestModel.column_family_name(), '%s."TestModel"' % (TestModel.__keyspace__,))
del TestModel.__keyspace__
with patch('cassandra.cqlengine.models.DEFAULT_KEYSPACE', None):
self.assertRaises(CQLEngineException, TestModel.column_family_name)
self.assertEqual(TestModel.column_family_name(include_keyspace=False), '"TestModel"')
class BuiltInAttributeConflictTest(unittest.TestCase):
"""tests Model definitions that conflict with built-in attributes/methods"""