From 2d22138c0dfa1f115f32d8d308a1dad3a79b0505 Mon Sep 17 00:00:00 2001 From: Daniel Dotsenko Date: Wed, 9 Apr 2014 10:49:04 -0700 Subject: [PATCH] fix for "ValueError: 'column_name' is not in list" error on sync_table for models with primary keys only https://github.com/cqlengine/cqlengine/issues/175 --- cqlengine/management.py | 25 ++++++++++++++++--- cqlengine/tests/management/test_management.py | 21 +++++++++++++++- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/cqlengine/management.py b/cqlengine/management.py index 7b40c30f..ed31eb57 100644 --- a/cqlengine/management.py +++ b/cqlengine/management.py @@ -65,6 +65,16 @@ def create_table(model, create_missing_keyspace=True): sync_table(model, create_missing_keyspace) def sync_table(model, create_missing_keyspace=True): + """ + Inspects the model and creates / updates the corresponding table and columns. + + Note that the attributes removed from the model are not deleted on the database. + They become effectively ignored by (will not show up on) the model. + + :param create_missing_keyspace: (Defaults to True) Flags to us that we need to create missing keyspace + mentioned in the model automatically. + :type create_missing_keyspace: bool + """ if model.__abstract__: raise CQLEngineException("cannot create table from abstract model") @@ -238,12 +248,19 @@ def get_fields(model): tmp = con.execute(query, {'ks_name': ks_name, 'col_family': col_family}, ONE) - column_indices = [tmp.columns.index('column_name'), tmp.columns.index('validator')] + # Tables containing only primary keys do not appear to create + # any entries in system.schema_columns, as only non-primary-key attributes + # appear to be inserted into the schema_columns table + if not tmp.results: + return [] + + column_name_positon = tmp.columns.index('column_name') + validator_positon = tmp.columns.index('validator') try: - type_index = tmp.columns.index('type') - return [Field(x[column_indices[0]], x[column_indices[1]]) for x in tmp.results if x[type_index] == 'regular'] + type_position = tmp.columns.index('type') + return [Field(x[column_name_positon], x[validator_positon]) for x in tmp.results if x[type_position] == 'regular'] except ValueError: - return [Field(x[column_indices[0]], x[column_indices[1]]) for x in tmp.results] + return [Field(x[column_name_positon], x[validator_positon]) for x in tmp.results] # convert to Field named tuples diff --git a/cqlengine/tests/management/test_management.py b/cqlengine/tests/management/test_management.py index 25f4d268..5179dee3 100644 --- a/cqlengine/tests/management/test_management.py +++ b/cqlengine/tests/management/test_management.py @@ -2,7 +2,7 @@ from mock import MagicMock, patch from cqlengine import ONE from cqlengine.exceptions import CQLEngineException -from cqlengine.management import create_table, delete_table, get_fields +from cqlengine.management import create_table, delete_table, get_fields, sync_table from cqlengine.tests.base import BaseCassEngTestCase from cqlengine.connection import ConnectionPool, Host from cqlengine import management @@ -77,6 +77,11 @@ class CapitalizedKeyModel(Model): secondKey = columns.Integer(primary_key=True) someData = columns.Text() +class PrimaryKeysOnlyModel(Model): + first_ey = columns.Integer(primary_key=True) + second_key = columns.Integer(primary_key=True) + + class CapitalizedKeyTest(BaseCassEngTestCase): def test_table_definition(self): @@ -142,3 +147,17 @@ class AddColumnTest(BaseCassEngTestCase): self.assertEqual(len(fields), 4) +class SyncTableTests(BaseCassEngTestCase): + + def setUp(self): + delete_table(PrimaryKeysOnlyModel) + + def test_sync_table_works_with_primary_keys_only_tables(self): + sync_table(PrimaryKeysOnlyModel) + + # primary-keys-only tables do not create entries in system.schema_columns + # table. Only non-primary keys are added to that table. + # Our code must deal with that eventuality properly (not crash) + # on subsequent runs of sync_table (which runs get_fields internally) + get_fields(PrimaryKeysOnlyModel) + sync_table(PrimaryKeysOnlyModel)