Beispiel #1
0
    def update(self, conn, **values):
        """Performs an update on the model instance. You can pass in values to
        set on the model for updating, or you can call without values to
        execute an update against any modified fields. If no fields on the
        model have been modified since loading, no query will be performed.
        Model validation is performed normally.

        It is possible to do a blind update, that is, to update a field
        without having first selected the object out of the database.
        See :ref:`Blind Updates <blind_updates>`

        :param conn: Cassandra connection wrapper used to execute the query.
        :type: cqlengine.ConnectionInterface subclass
        :param kwargs: Model column values as keyword arguments.

        :return: self
        """
        for k, v in values.items():
            col = self._columns.get(k)

            # check for nonexistant columns
            if col is None:
                raise ValidationError(
                    "{0}.{1} has no column named: {2}".format(
                        self.__module__,
                        self.__class__.__name__,
                        k,
                    ))

            # check for primary key update attempts
            if col.is_primary_key:
                raise ValidationError(
                    "Cannot apply update to primary key '{0}' for "
                    "{1}.{2}".format(
                        k,
                        self.__module__,
                        self.__class__.__name__,
                    ))

            setattr(self, k, v)

        self.validate()
        q = query.UpdateDMLQuery(self.__class__,
                                 self,
                                 ttl=self._ttl,
                                 timestamp=self._timestamp,
                                 consistency=self.__consistency__,
                                 conditional=self._conditional,
                                 timeout=self._timeout,
                                 if_exists=self._if_exists)
        self._execute_query(conn, q)
        self._set_persisted()
        self._timestamp = None

        return self
Beispiel #2
0
 def validate(self, value):
     val = super(List, self).validate(value)
     if val is None:
         return
     if not isinstance(val, (set, list, tuple)):
         raise ValidationError('{0} {1} is not a list object'.format(
             self.column_name, val))
     if None in val:
         raise ValidationError("{0} None is not allowed in a list".format(
             self.column_name))
     return [self.value_col.validate(v) for v in val]
Beispiel #3
0
 def validate(self, value):
     val = super(Map, self).validate(value)
     if val is None:
         return
     if not isinstance(val, (dict, util.OrderedMap)):
         raise ValidationError('{0} {1} is not a dict object'.format(
             self.column_name, val))
     if None in val:
         raise ValidationError("{0} None is not allowed in a map".format(
             self.column_name))
     # TODO: stop doing this conversion because it doesn't support non-hashable collections as keys (cassandra does)
     # will need to start using the cassandra.util types in the next major rev (PYTHON-494)
     return dict((self.key_col.validate(k), self.value_col.validate(v))
                 for k, v in val.items())
Beispiel #4
0
 def __init__(self, value):
     """
     :param value: the time to create bounding time uuid from
     :type value: datetime
     """
     if not isinstance(value, datetime):
         raise ValidationError("datetime instance is required")
     super(TimeUUIDQueryFunction, self).__init__(value)
Beispiel #5
0
 def validate(self, value):
     value = super(Text, self).validate(value)
     if not isinstance(value,
                       (six.string_types, bytearray)) and value is not None:
         raise ValidationError('{0} {1} is not a string'.format(
             self.column_name, type(value)))
     if self.max_length is not None:
         if value and len(value) > self.max_length:
             raise ValidationError(
                 '{0} is longer than {1} characters'.format(
                     self.column_name, self.max_length))
     if self.min_length:
         if (self.min_length and not value) or len(value) < self.min_length:
             raise ValidationError(
                 '{0} is shorter than {1} characters'.format(
                     self.column_name, self.min_length))
     return value
Beispiel #6
0
 def validate(self, value):
     value = super(BaseCollectionColumn, self).validate(value)
     # It is dangerous to let collections have more than 65535.
     # See: https://issues.apache.org/jira/browse/CASSANDRA-5428
     if value is not None and len(value) > 65535:
         raise ValidationError(
             "{0} Collection can't have more than 65535 elements.".format(
                 self.column_name))
     return value
Beispiel #7
0
 def validate(self, value):
     value = super(BaseFloat, self).validate(value)
     if value is None:
         return
     try:
         return float(value)
     except (TypeError, ValueError):
         raise ValidationError("{0} {1} is not a valid float".format(
             self.column_name, value))
Beispiel #8
0
 def validate(self, value):
     val = super(Tuple, self).validate(value)
     if val is None:
         return
     if len(val) > len(self.types):
         raise ValidationError(
             "Value %r has more fields than tuple definition (%s)" %
             (val, ', '.join(t for t in self.types)))
     return tuple(t.validate(v) for t, v in zip(self.types, val))
Beispiel #9
0
    def __init__(self, types, **kwargs):
        """
        :param types: a sequence of sub types in this collection
        """
        instances = []
        for t in types:
            inheritance_comparator = issubclass if isinstance(
                t, type) else isinstance
            if not inheritance_comparator(t, Column):
                raise ValidationError("%s is not a column class" % (t, ))
            if t.db_type is None:
                raise ValidationError("%s is an abstract type" % (t, ))
            inst = t() if isinstance(t, type) else t
            if isinstance(t, BaseCollectionColumn):
                inst._freeze_db_type()
            instances.append(inst)

        self.types = instances
        super(BaseCollectionColumn, self).__init__(**kwargs)
Beispiel #10
0
 def validate(self, value):
     val = super(VarInt, self).validate(value)
     if val is None:
         return
     try:
         return int(val)
     except (TypeError, ValueError):
         raise ValidationError(
             "{0} {1} can't be converted to integral value".format(
                 self.column_name, value))
Beispiel #11
0
 def validate(self, value):
     """
     Returns a cleaned and validated value. Raises a ValidationError
     if there's a problem
     """
     if value is None:
         if self.required:
             raise ValidationError(
                 '{0} - None values are not allowed'.format(
                     self.column_name or self.db_field))
     return value
Beispiel #12
0
    def create(cls, conn, **kwargs):
        """Create an instance of this model in the database.

        Takes the model column values as keyword arguments.

        Returns the instance.
        """
        extra_columns = set(kwargs.keys()) - set(cls._columns.keys())
        if extra_columns:
            raise ValidationError(
                "Incorrect columns passed: {0}".format(extra_columns))
        return cls.objects.create(conn, **kwargs)
Beispiel #13
0
    def validate(self, value):
        val = super(Set, self).validate(value)
        if val is None:
            return
        types = (set, util.SortedSet) if self.strict else (set, util.SortedSet,
                                                           list, tuple)
        if not isinstance(val, types):
            if self.strict:
                raise ValidationError('{0} {1} is not a set object'.format(
                    self.column_name, val))
            else:
                raise ValidationError(
                    '{0} {1} cannot be coerced to a set object'.format(
                        self.column_name, val))

        if None in val:
            raise ValidationError("{0} None not allowed in a set".format(
                self.column_name))
        # TODO: stop doing this conversion because it doesn't support non-hashable collections as keys (cassandra does)
        # will need to start using the cassandra.util types in the next major rev (PYTHON-494)
        return set(self.value_col.validate(v) for v in val)
Beispiel #14
0
 def validate(self, value):
     from decimal import Decimal as _Decimal
     from decimal import InvalidOperation
     val = super(Decimal, self).validate(value)
     if val is None:
         return
     try:
         return _Decimal(repr(val)) if isinstance(val,
                                                  float) else _Decimal(val)
     except InvalidOperation:
         raise ValidationError(
             "{0} '{1}' can't be coerced to decimal".format(
                 self.column_name, val))
Beispiel #15
0
    def create(cls, conn, **kwargs):
        """Create an instance of this model in the database.

        :param conn: Cassandra connection wrapper used to execute the query.
        :type: cqlengine.ConnectionInterface subclass
        :param kwargs: Model column values as keyword arguments.

        :return: The instance created.
        """
        extra_columns = set(kwargs.keys()) - set(cls._columns.keys())
        if extra_columns:
            raise ValidationError(
                "Incorrect columns passed: {0}".format(extra_columns))
        return cls.objects.create(conn, **kwargs)
Beispiel #16
0
 def validate(self, value):
     val = super(UUID, self).validate(value)
     if val is None:
         return
     if isinstance(val, _UUID):
         return val
     if isinstance(val, six.string_types):
         try:
             return _UUID(val)
         except ValueError:
             # fall-through to error
             pass
     raise ValidationError("{0} {1} is not a valid uuid".format(
         self.column_name, value))
Beispiel #17
0
 def __init__(self, value_type, strict=True, default=set, **kwargs):
     """
     :param value_type: a column class indicating the types of the value
     :param strict: sets whether non set values will be coerced to set
         type on validation, or raise a validation error, defaults to True
     """
     self.strict = strict
     super(Set, self).__init__((value_type, ), default=default, **kwargs)
     self.value_col = self.types[0]
     if not self.value_col._python_type_hashable:
         raise ValidationError(
             "Cannot create a Set with unhashable value type (see PYTHON-494)"
         )
     self.db_type = 'set<{0}>'.format(self.value_col.db_type)
Beispiel #18
0
    def to_database(self, value):
        value = super(DateTime, self).to_database(value)
        if value is None:
            return
        if not isinstance(value, datetime):
            if isinstance(value, date):
                value = datetime(value.year, value.month, value.day)
            else:
                raise ValidationError(
                    "{0} '{1}' is not a datetime object".format(
                        self.column_name, value))
        epoch = datetime(1970, 1, 1, tzinfo=value.tzinfo)
        offset = get_total_seconds(
            epoch.tzinfo.utcoffset(epoch)) if epoch.tzinfo else 0

        return int((get_total_seconds(value - epoch) - offset) * 1000)
Beispiel #19
0
    def validate(self, value):
        """ Only allow ASCII and None values.

        Check against US-ASCII, a.k.a. 7-bit ASCII, a.k.a. ISO646-US, a.k.a.
        the Basic Latin block of the Unicode character set.

        Source: https://github.com/apache/cassandra/blob
        /3dcbe90e02440e6ee534f643c7603d50ca08482b/src/java/org/apache/cassandra
        /serializers/AsciiSerializer.java#L29
        """
        value = super(Ascii, self).validate(value)
        if value:
            charset = value if isinstance(value,
                                          (bytearray, )) else map(ord, value)
            if not set(range(128)).issuperset(charset):
                raise ValidationError(
                    '{!r} is not an ASCII string.'.format(value))
        return value
Beispiel #20
0
    def __init__(self, key_type, value_type, default=dict, **kwargs):
        """
        :param key_type: a column class indicating the types of the key
        :param value_type: a column class indicating the types of the value
        """
        super(Map, self).__init__((key_type, value_type),
                                  default=default,
                                  **kwargs)
        self.key_col = self.types[0]
        self.value_col = self.types[1]

        if not self.key_col._python_type_hashable:
            raise ValidationError(
                "Cannot create a Map with unhashable key type (see PYTHON-494)"
            )

        self.db_type = 'map<{0}, {1}>'.format(self.key_col.db_type,
                                              self.value_col.db_type)
Beispiel #21
0
    def update(self, conn, **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()
        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__,
                    )
                )

            # 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:
            self._execute_statement(conn, 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,
            )
            self._execute_statement(conn, ds)