def update(self, **values): """ Updates the rows in this queryset """ if not values: return nulled_columns = set() us = UpdateStatement(self.column_family_name, where=self._where, ttl=self._ttl, timestamp=self._timestamp) for name, val in values.items(): col = self.model._columns.get(name) # check for nonexistant columns if col is None: raise ValidationError("{}.{} has no column named: {}".format(self.__module__, self.model.__name__, name)) # check for primary key update attempts if col.is_primary_key: raise ValidationError("Cannot apply update to primary key '{}' for {}.{}".format(name, self.__module__, self.model.__name__)) val = col.validate(val) if val is None: nulled_columns.add(name) continue # add the update statements if isinstance(col, Counter): # TODO: implement counter updates raise NotImplementedError else: us.add_assignment_clause(AssignmentClause(name, col.to_database(val))) if us.assignments: self._execute(us) if nulled_columns: ds = DeleteStatement(self.column_family_name, fields=nulled_columns, where=self._where) self._execute(ds)
def update(self): """ updates a row. This is a blind update call. All validation and cleaning needs to happen prior to calling this. """ if self.instance is None: raise CQLEngineException("DML Query intance attribute is None") assert type(self.instance) == self.model static_update_only = True statement = UpdateStatement(self.column_family_name, ttl=self._ttl, timestamp=self._timestamp, transactions=self._transaction) #get defined fields and their column names for name, col in self.model._columns.items(): if not col.is_primary_key: val = getattr(self.instance, name, None) val_mgr = self.instance._values[name] # don't update something that is null if val is None: continue # don't update something if it hasn't changed if not val_mgr.changed and not isinstance(col, Counter): continue static_update_only = (static_update_only and col.static) if isinstance(col, (BaseContainerColumn, Counter)): # get appropriate clause if isinstance(col, List): klass = ListUpdateClause elif isinstance(col, Map): klass = MapUpdateClause elif isinstance(col, Set): klass = SetUpdateClause elif isinstance(col, Counter): klass = CounterUpdateClause else: raise RuntimeError # do the stuff clause = klass(col.db_field_name, val, previous=val_mgr.previous_value, column=col) if clause.get_context_size() > 0: statement.add_assignment_clause(clause) else: statement.add_assignment_clause(AssignmentClause( col.db_field_name, col.to_database(val) )) if statement.get_context_size() > 0 or self.instance._has_counter: for name, col in self.model._primary_keys.items(): if static_update_only and (not col.partition_key): continue statement.add_where_clause(WhereClause( col.db_field_name, EqualsOperator(), col.to_database(getattr(self.instance, name)) )) self._execute(statement) self._delete_null_columns()
def test_context_update(self): us = UpdateStatement('table') us.add_assignment_clause(AssignmentClause('a', 'b')) us.add_assignment_clause(AssignmentClause('c', 'd')) us.add_where_clause(WhereClause('a', EqualsOperator(), 'x')) us.update_context_id(3) self.assertEqual( six.text_type(us), 'UPDATE table SET "a" = %(4)s, "c" = %(5)s WHERE "a" = %(3)s') self.assertEqual(us.get_context(), {'4': 'b', '5': 'd', '3': 'x'})
def update(self, **values): """ Updates the rows in this queryset """ if not values: return nulled_columns = set() us = UpdateStatement(self.column_family_name, where=self._where, ttl=self._ttl, timestamp=self._timestamp, transactions=self._transaction) for name, val in values.items(): col_name, col_op = self._parse_filter_arg(name) col = self.model._columns.get(col_name) # check for nonexistant columns if col is None: raise ValidationError("{}.{} has no column named: {}".format(self.__module__, self.model.__name__, col_name)) # check for primary key update attempts if col.is_primary_key: raise ValidationError("Cannot apply update to primary key '{}' for {}.{}".format(col_name, self.__module__, self.model.__name__)) # we should not provide default values in this use case. val = col.validate(val) if val is None: nulled_columns.add(col_name) continue # add the update statements if isinstance(col, Counter): # TODO: implement counter updates raise NotImplementedError elif isinstance(col, (List, Set, Map)): if isinstance(col, List): klass = ListUpdateClause elif isinstance(col, Set): klass = SetUpdateClause elif isinstance(col, Map): klass = MapUpdateClause else: raise RuntimeError us.add_assignment_clause(klass(col_name, col.to_database(val), operation=col_op)) else: us.add_assignment_clause(AssignmentClause( col_name, col.to_database(val))) if us.assignments: self._execute(us) if nulled_columns: ds = DeleteStatement(self.column_family_name, fields=nulled_columns, where=self._where) self._execute(ds)
def test_context_update(self): us = UpdateStatement('table') us.add_assignment_clause(AssignmentClause('a', 'b')) us.add_assignment_clause(AssignmentClause('c', 'd')) us.add_where_clause(WhereClause('a', EqualsOperator(), 'x')) us.update_context_id(3) self.assertEqual(six.text_type(us), 'UPDATE table SET "a" = %(4)s, "c" = %(5)s WHERE "a" = %(3)s') self.assertEqual(us.get_context(), {'4': 'b', '5': 'd', '3': 'x'})
def test_context_update(self): us = UpdateStatement("table") us.add_assignment_clause(AssignmentClause("a", "b")) us.add_assignment_clause(AssignmentClause("c", "d")) us.add_where_clause(WhereClause("a", EqualsOperator(), "x")) us.update_context_id(3) self.assertEqual(unicode(us), 'UPDATE table SET "a" = :4, "c" = :5 WHERE "a" = :3') self.assertEqual(us.get_context(), {"4": "b", "5": "d", "3": "x"})
def test_table_rendering(self): """ tests that fields are properly added to the select statement """ us = UpdateStatement('table') self.assertTrue( six.text_type(us).startswith('UPDATE table SET'), six.text_type(us)) self.assertTrue(str(us).startswith('UPDATE table SET'), str(us))
def test_rendering(self): us = UpdateStatement('table') us.add_assignment_clause(AssignmentClause('a', 'b')) us.add_assignment_clause(AssignmentClause('c', 'd')) us.add_where_clause(WhereClause('a', EqualsOperator(), 'x')) self.assertEqual( six.text_type(us), 'UPDATE table SET "a" = %(0)s, "c" = %(1)s WHERE "a" = %(2)s', six.text_type(us))
def update(self, **values): """ Updates the rows in this queryset """ if not values: return nulled_columns = set() us = UpdateStatement(self.column_family_name, where=self._where, ttl=self._ttl, timestamp=self._timestamp) for name, val in values.items(): col_name, col_op = self._parse_filter_arg(name) col = self.model._columns.get(col_name) # check for nonexistant columns if col is None: raise ValidationError("{}.{} has no column named: {}".format( self.__module__, self.model.__name__, col_name)) # check for primary key update attempts if col.is_primary_key: raise ValidationError( "Cannot apply update to primary key '{}' for {}.{}".format( col_name, self.__module__, self.model.__name__)) val = col.validate(val) if val is None: nulled_columns.add(col_name) continue # add the update statements if isinstance(col, Counter): # TODO: implement counter updates raise NotImplementedError elif isinstance(col, (List, Set, Map)): if isinstance(col, List): klass = ListUpdateClause elif isinstance(col, Set): klass = SetUpdateClause elif isinstance(col, Map): klass = MapUpdateClause else: raise RuntimeError us.add_assignment_clause( klass(col_name, col.to_database(val), operation=col_op)) else: us.add_assignment_clause( AssignmentClause(col_name, col.to_database(val))) if us.assignments: self._execute(us) if nulled_columns: ds = DeleteStatement(self.column_family_name, fields=nulled_columns, where=self._where) self._execute(ds)
def test_context(self): us = UpdateStatement("table") us.add_assignment_clause(AssignmentClause("a", "b")) us.add_assignment_clause(AssignmentClause("c", "d")) us.add_where_clause(WhereClause("a", EqualsOperator(), "x")) self.assertEqual(us.get_context(), {"0": "b", "1": "d", "2": "x"})
def test_rendering(self): us = UpdateStatement("table") us.add_assignment_clause(AssignmentClause("a", "b")) us.add_assignment_clause(AssignmentClause("c", "d")) us.add_where_clause(WhereClause("a", EqualsOperator(), "x")) self.assertEqual(unicode(us), 'UPDATE table SET "a" = :0, "c" = :1 WHERE "a" = :2', unicode(us))
def test_context(self): us = UpdateStatement('table') us.add_assignment_clause(AssignmentClause('a', 'b')) us.add_assignment_clause(AssignmentClause('c', 'd')) us.add_where_clause(WhereClause('a', EqualsOperator(), 'x')) self.assertEqual(us.get_context(), {'0': 'b', '1': 'd', '2': 'x'})
def test_update_list_append_with_empty_list(self): us = UpdateStatement('table') us.add_assignment_clause(ListUpdateClause('a', List.Quoter([]), operation='append')) self.assertEqual(six.text_type(us), 'UPDATE table SET "a" = "a" + %(0)s')
def test_update_empty_set_removal_does_not_assign(self): us = UpdateStatement('table') us.add_assignment_clause(SetUpdateClause('a', Set.Quoter(set()), operation='remove')) self.assertEqual(six.text_type(us), 'UPDATE table SET "a" = "a" - %(0)s')
def test_additional_rendering(self): us = UpdateStatement('table', ttl=60) us.add_assignment_clause(AssignmentClause('a', 'b')) us.add_where_clause(WhereClause('a', EqualsOperator(), 'x')) self.assertIn('USING TTL 60', six.text_type(us))
def test_rendering(self): us = UpdateStatement('table') us.add_assignment_clause(AssignmentClause('a', 'b')) us.add_assignment_clause(AssignmentClause('c', 'd')) us.add_where_clause(WhereClause('a', EqualsOperator(), 'x')) self.assertEqual(six.text_type(us), 'UPDATE table SET "a" = %(0)s, "c" = %(1)s WHERE "a" = %(2)s', six.text_type(us))
def test_additional_rendering(self): us = UpdateStatement("table", ttl=60) us.add_assignment_clause(AssignmentClause("a", "b")) us.add_where_clause(WhereClause("a", EqualsOperator(), "x")) self.assertIn("USING TTL 60", unicode(us))
def save(self, *objects): """Flush all pending changes to Cassandra. objects -- if not None, only operate on this or these object(s) """ updates = set() counter_updates = set() creates = set() counter_creates = set() for model_class, by_key in self.instances_by_class.iteritems(): for key, instance in by_key.iteritems(): if hasattr(instance, '_created') and instance._created: if model_class.id_mapped_class._has_counter: counter_creates.add(instance) else: creates.add(instance) elif hasattr(instance, '_dirties'): if model_class.id_mapped_class._has_counter: counter_updates.add(instance) else: updates.add(instance) if objects: updates = updates and objects counter_updates = counter_updates and objects creates = creates and objects counter_creates = counter_creates and objects with BatchQuery() as batch: for create in creates: # Note we skip a lot of cqlengine code and create the # insert statement directly. # (this is the non-optimized code that is replaced below) #key_names = create.id_mapped_class._columns.keys() #arg = {name: getattr(create, name) for name in key_names} #create.id_mapped_class.batch(batch).create(**arg) # (end non-optimized code) # (begin optimized) # note: it might save time to memoize column family name # note: cqlengine-session doesn't yet support 'ttl' insert = InsertStatement(create.id_mapped_class.column_family_name())#, ttl=self._ttl) for name, col in create.id_mapped_class._columns.items(): val = col.validate(getattr(create, name)) if col._val_is_null(val): continue insert.add_assignment_clause(AssignmentClause( col.db_field_name, col.to_database(val))) # skip query execution if it's empty # caused by pointless update queries if not insert.is_empty: batch.add_query(insert) # (end optimized) del create._created try: del create._dirties except AttributeError: pass for update in updates: key_names = update._primary_keys.keys() arg = {name: getattr(update, name) for name in key_names} dirties = update._dirties update.id_mapped_class.objects(**arg).batch(batch).update(**dirties) del update._dirties # It would seem that batch does not work with counter? #with BatchQuery() as batch: for create in counter_creates: primary_key_names = create.id_mapped_class._primary_keys.keys() arg = {name: getattr(create, name) for name in primary_key_names} instance = create.id_mapped_class.create(**arg) for name, col in create.id_mapped_class._columns.items(): if isinstance(col, columns.Counter): val = getattr(create, name) setattr(instance, name, val) del create._created try: del create._dirties except AttributeError: pass instance.update() for update in counter_updates: statement = UpdateStatement(update.id_mapped_class.column_family_name())#, ttl=self._ttl) for name, value in update._dirties.items(): col = update.id_mapped_class._columns[name] clause = CounterUpdateClause(col.db_field_name, value, 0, column=col) statement.add_assignment_clause(clause) for name, col in update.id_mapped_class._primary_keys.items(): statement.add_where_clause(WhereClause( col.db_field_name, EqualsOperator(), col.to_database(getattr(update, name)) )) params = statement.get_context() statement = SimpleStatement(str(statement)) cqlengine.connection.get_session().execute(statement, params) del update._dirties # for delete in self.deletes: # raise NotImplementedError for callable, args, kwargs in self.call_after_save: callable(*args, **kwargs) self.call_after_save = []
def test_update_set_add(self): us = UpdateStatement('table') us.add_assignment_clause(SetUpdateClause('a', Set.Quoter(set([1])), operation='add')) self.assertEqual(six.text_type(us), 'UPDATE table SET "a" = "a" + %(0)s')