def delete(self): """ Deletes one instance """ if self.instance is None: raise CQLEngineException("DML Query instance attribute is None") ds = DeleteStatement(self.column_family_name, timestamp=self._timestamp, conditionals=self._conditional, if_exists=self._if_exists) for name, col in self.model._primary_keys.items(): val = getattr(self.instance, name) if val is None and not col.parition_key: continue ds.add_where(col, EqualsOperator(), val) self._execute(ds)
def delete(self): """ Deletes one instance """ if self.instance is None: raise CQLEngineException("DML Query instance attribute is None") ds = DeleteStatement(self.column_family_name, timestamp=self._timestamp) for name, col in self.model._primary_keys.items(): if (not col.partition_key) and (getattr(self.instance, name) is None): continue ds.add_where_clause( WhereClause(col.db_field_name, EqualsOperator(), col.to_database(getattr(self.instance, name))) ) self._execute(ds)
def test_context_update(self): ds = DeleteStatement('table', None) ds.add_field(MapDeleteClause('d', {1: 2}, {1: 2, 3: 4})) ds.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) ds.update_context_id(7) self.assertEqual(six.text_type(ds), 'DELETE "d"[%(8)s] FROM table WHERE "a" = %(7)s') self.assertEqual(ds.get_context(), {'7': 'b', '8': 3})
def _delete_null_columns(self): """ executes a delete query to remove columns that have changed to null """ ds = DeleteStatement(self.column_family_name, if_exists=self._if_exists) deleted_fields = False for _, v in self.instance._values.items(): col = v.column if v.deleted: ds.add_field(col.db_field_name) deleted_fields = True elif isinstance(col, columns.Map): uc = MapDeleteClause(col.db_field_name, v.value, v.previous_value) if uc.get_context_size() > 0: ds.add_field(uc) deleted_fields = True if deleted_fields: for name, col in self.model._primary_keys.items(): ds.add_where_clause(WhereClause( col.db_field_name, EqualsOperator(), col.to_database(getattr(self.instance, name)) )) self._execute(ds)
def test_insert_statement_execute(self): """ Test to verify the execution of BaseCQLStatements using connection.execute @since 3.10 @jira_ticket PYTHON-505 @expected_result inserts a row in C*, updates the rows and then deletes all the rows using BaseCQLStatements @test_category data_types:object_mapper """ partition = uuid4() cluster = 1 self._insert_statement(partition, cluster) # Verifying update statement where = [ WhereClause('partition', EqualsOperator(), partition), WhereClause('cluster', EqualsOperator(), cluster) ] st = UpdateStatement(self.table_name, where=where) st.add_assignment(Column(db_field='count'), 2) st.add_assignment(Column(db_field='text'), "text_for_db_update") st.add_assignment(Column(db_field='text_set'), set(("foo_update", "bar_update"))) st.add_assignment(Column(db_field='text_list'), ["foo_update", "bar_update"]) st.add_assignment(Column(db_field='text_map'), { "foo": '3', "bar": '4' }) execute(st) self._verify_statement(st) # Verifying delete statement execute(DeleteStatement(self.table_name, where=where)) self.assertEqual(TestQueryUpdateModel.objects.count(), 0)
def _delete_null_columns(self): """ executes a delete query to remove columns that have changed to null """ ds = DeleteStatement(self.column_family_name) deleted_fields = False for _, v in self.instance._values.items(): col = v.column if v.deleted: ds.add_field(col.db_field_name) deleted_fields = True elif isinstance(col, columns.Map): uc = MapDeleteClause(col.db_field_name, v.value, v.previous_value) if uc.get_context_size() > 0: ds.add_field(uc) deleted_fields = True if deleted_fields: for name, col in self.model._primary_keys.items(): ds.add_where_clause( WhereClause(col.db_field_name, EqualsOperator(), col.to_database(getattr(self.instance, name)))) self._execute(ds)
def test_partition_key_index(self): """ Test to ensure that statement partition key generation is in the correct order @since 3.2 @jira_ticket PYTHON-535 @expected_result . @test_category object_mapper """ self._check_partition_value_generation(BasicModel, SelectStatement(BasicModel.__table_name__)) self._check_partition_value_generation(BasicModel, DeleteStatement(BasicModel.__table_name__)) self._check_partition_value_generation(BasicModelMulti, SelectStatement(BasicModelMulti.__table_name__)) self._check_partition_value_generation(BasicModelMulti, DeleteStatement(BasicModelMulti.__table_name__)) self._check_partition_value_generation(ComplexModelRouting, SelectStatement(ComplexModelRouting.__table_name__)) self._check_partition_value_generation(ComplexModelRouting, DeleteStatement(ComplexModelRouting.__table_name__)) self._check_partition_value_generation(BasicModel, SelectStatement(BasicModel.__table_name__), reverse=True) self._check_partition_value_generation(BasicModel, DeleteStatement(BasicModel.__table_name__), reverse=True) self._check_partition_value_generation(BasicModelMulti, SelectStatement(BasicModelMulti.__table_name__), reverse=True) self._check_partition_value_generation(BasicModelMulti, DeleteStatement(BasicModelMulti.__table_name__), reverse=True) self._check_partition_value_generation(ComplexModelRouting, SelectStatement(ComplexModelRouting.__table_name__), reverse=True) self._check_partition_value_generation(ComplexModelRouting, DeleteStatement(ComplexModelRouting.__table_name__), reverse=True)
def test_table_rendering(self): ds = DeleteStatement('table', None) self.assertTrue( six.text_type(ds).startswith('DELETE FROM table'), six.text_type(ds)) self.assertTrue(str(ds).startswith('DELETE FROM table'), str(ds))
def test_none_fields_rendering(self): """ tests that a '*' is added if no fields are passed in """ ds = DeleteStatement('table', None) self.assertTrue( six.text_type(ds).startswith('DELETE FROM'), six.text_type(ds)) self.assertTrue(str(ds).startswith('DELETE FROM'), str(ds))
def test_single_field_is_listified(self): """ tests that passing a string field into the constructor puts it into a list """ ds = DeleteStatement('table', 'field') self.assertEqual(len(ds.fields), 1) self.assertEqual(ds.fields[0].field, 'field')
def test_range_deletion_rendering(self): ds = DeleteStatement('table', None) ds.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) ds.add_where_clause(WhereClause('created_at', GreaterThanOrEqualOperator(), '0')) ds.add_where_clause(WhereClause('created_at', LessThanOrEqualOperator(), '10')) self.assertEqual(six.text_type(ds), 'DELETE FROM table WHERE "a" = %(0)s AND "created_at" >= %(1)s AND "created_at" <= %(2)s', six.text_type(ds)) ds = DeleteStatement('table', None) ds.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) ds.add_where_clause(WhereClause('created_at', InOperator(), ['0', '10', '20'])) self.assertEqual(six.text_type(ds), 'DELETE FROM table WHERE "a" = %(0)s AND "created_at" IN %(1)s', six.text_type(ds))
def test_range_deletion_rendering(self): ds = DeleteStatement('table', None) ds.add_where(Column(db_field='a'), EqualsOperator(), 'b') ds.add_where(Column(db_field='created_at'), GreaterThanOrEqualOperator(), '0') ds.add_where(Column(db_field='created_at'), LessThanOrEqualOperator(), '10') self.assertEqual( six.text_type(ds), 'DELETE FROM table WHERE "a" = %(0)s AND "created_at" >= %(1)s AND "created_at" <= %(2)s', six.text_type(ds)) ds = DeleteStatement('table', None) ds.add_where(Column(db_field='a'), EqualsOperator(), 'b') ds.add_where(Column(db_field='created_at'), InOperator(), ['0', '10', '20']) self.assertEqual( six.text_type(ds), 'DELETE FROM table WHERE "a" = %(0)s AND "created_at" IN %(1)s', six.text_type(ds)) ds = DeleteStatement('table', None) ds.add_where(Column(db_field='a'), NotEqualsOperator(), 'b') self.assertEqual(six.text_type(ds), 'DELETE FROM table WHERE "a" != %(0)s', six.text_type(ds))
def test_context(self): ds = DeleteStatement('table', None) ds.add_where(Column(db_field='a'), EqualsOperator(), 'b') self.assertEqual(ds.get_context(), {'0': 'b'})
async def async_update(self, **values): if not values: return nulled_columns = set() updated_columns = set() us = UpdateStatement( self.column_family_name, where=self._where, ttl=self._ttl, timestamp=self._timestamp, conditionals=self._conditional, if_exists=self._if_exists, ) 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( "{0}.{1} has no column named: {2}".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 '{0}' for {1}.{2}". format(col_name, self.__module__, self.model.__name__)) if col_op == "remove" and isinstance(col, columns.Map): if not isinstance(val, set): raise ValidationError( "Cannot apply update operation '{0}' on column '{1}' with value '{2}'. A set is required." .format(col_op, col_name, val)) val = {v: None for v in val} else: # we should not provide default values in this use case. val = col.validate(val) if val is None: nulled_columns.add(col_name) continue us.add_update(col, val, operation=col_op) updated_columns.add(col_name) if us.assignments: await self._async_execute(us) if nulled_columns: delete_conditional = ([ condition for condition in self._conditional if condition.field not in updated_columns ] if self._conditional else None) ds = DeleteStatement( self.column_family_name, fields=nulled_columns, where=self._where, conditionals=delete_conditional, if_exists=self._if_exists, ) await self._async_execute(ds)
def query_parameters_from_delete(collection, filters): where_clauses = where_clauses_from_filters(filters) statement = DeleteStatement( table=collection, where=where_clauses ) return str(statement), statement.get_context()
def delete_query(table: str, filters: list): where_clauses = generate_where_clause_from_filters(filters) statement = DeleteStatement(table, where_clauses) return str(statement), statement.get_context()
def test_where_clause_rendering(self): ds = DeleteStatement('table', None) ds.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) self.assertEqual(six.text_type(ds), 'DELETE FROM table WHERE "a" = %(0)s', six.text_type(ds))
def test_context(self): ds = DeleteStatement('table', None) ds.add_where_clause(WhereClause('a', EqualsOperator(), 'b')) self.assertEqual(ds.get_context(), {'0': 'b'})
def update(self, **values): """ Performs an update on the row selected by the queryset. Include values to update in the update like so: .. code-block:: python Model.objects(key=n).update(value='x') Passing in updates for columns which are not part of the model will raise a ValidationError. Per column validation will be performed, but instance level validation will not (i.e., `Model.validate` is not called). This is sometimes referred to as a blind update. For example: .. code-block:: python class User(Model): id = Integer(primary_key=True) name = Text() setup(["localhost"], "test") sync_table(User) u = User.create(id=1, name="jon") User.objects(id=1).update(name="Steve") # sets name to null User.objects(id=1).update(name=None) Also supported is blindly adding and removing elements from container columns, without loading a model instance from Cassandra. Using the syntax `.update(column_name={x, y, z})` will overwrite the contents of the container, like updating a non container column. However, adding `__<operation>` to the end of the keyword arg, makes the update call add or remove items from the collection, without overwriting then entire column. Given the model below, here are the operations that can be performed on the different container columns: .. code-block:: python class Row(Model): row_id = columns.Integer(primary_key=True) set_column = columns.Set(Integer) list_column = columns.List(Integer) map_column = columns.Map(Integer, Integer) :class:`~cqlengine.columns.Set` - `add`: adds the elements of the given set to the column - `remove`: removes the elements of the given set to the column .. code-block:: python # add elements to a set Row.objects(row_id=5).update(set_column__add={6}) # remove elements to a set Row.objects(row_id=5).update(set_column__remove={4}) :class:`~cqlengine.columns.List` - `append`: appends the elements of the given list to the end of the column - `prepend`: prepends the elements of the given list to the beginning of the column .. code-block:: python # append items to a list Row.objects(row_id=5).update(list_column__append=[6, 7]) # prepend items to a list Row.objects(row_id=5).update(list_column__prepend=[1, 2]) :class:`~cqlengine.columns.Map` - `update`: adds the given keys/values to the columns, creating new entries if they didn't exist, and overwriting old ones if they did .. code-block:: python # add items to a map Row.objects(row_id=5).update(map_column__update={1: 2, 3: 4}) """ 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, columns.Counter): # TODO: implement counter updates raise NotImplementedError elif isinstance(col, (columns.List, columns.Set, columns.Map)): if isinstance(col, columns.List): klass = ListUpdateClause elif isinstance(col, columns.Set): klass = SetUpdateClause elif isinstance(col, columns.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_range_deletion_rendering(self): ds = DeleteStatement('table', None) ds.add_where(Column(db_field='a'), EqualsOperator(), 'b') ds.add_where(Column(db_field='created_at'), GreaterThanOrEqualOperator(), '0') ds.add_where(Column(db_field='created_at'), LessThanOrEqualOperator(), '10') self.assertEqual(six.text_type(ds), 'DELETE FROM table WHERE "a" = %(0)s AND "created_at" >= %(1)s AND "created_at" <= %(2)s', six.text_type(ds)) ds = DeleteStatement('table', None) ds.add_where(Column(db_field='a'), EqualsOperator(), 'b') ds.add_where(Column(db_field='created_at'), InOperator(), ['0', '10', '20']) self.assertEqual(six.text_type(ds), 'DELETE FROM table WHERE "a" = %(0)s AND "created_at" IN %(1)s', six.text_type(ds)) ds = DeleteStatement('table', None) ds.add_where(Column(db_field='a'), NotEqualsOperator(), 'b') self.assertEqual(six.text_type(ds), 'DELETE FROM table WHERE "a" != %(0)s', six.text_type(ds))