Models¶
Users of versions < 0.4, please read this post before upgrading: Breaking Changes
A model is a python class representing a CQL table.
Examples¶
This example defines a Person table, with the columns first_name
and last_name
from cqlengine import columns
from cqlengine.models import Model
class Person(Model):
id = columns.UUID(primary_key=True)
first_name = columns.Text()
last_name = columns.Text()
The Person model would create this CQL table:
CREATE TABLE cqlengine.person (
id uuid,
first_name text,
last_name text,
PRIMARY KEY (id)
)
Here’s an example of a comment table created with clustering keys, in descending order:
from cqlengine import columns
from cqlengine.models import Model
class Comment(Model):
photo_id = columns.UUID(primary_key=True)
comment_id = columns.TimeUUID(primary_key=True, clustering_order="DESC")
comment = columns.Text()
The Comment model’s create table
would look like the following:
CREATE TABLE comment (
photo_id uuid,
comment_id timeuuid,
comment text,
PRIMARY KEY (photo_id, comment_id)
) WITH CLUSTERING ORDER BY (comment_id DESC)
To sync the models to the database, you may do the following:
from cqlengine.management import sync_table
sync_table(Person)
sync_table(Comment)
Columns¶
Columns in your models map to columns in your CQL table. You define CQL columns by defining column attributes on your model classes. For a model to be valid it needs at least one primary key column and one non-primary key column.
Just as in CQL, the order you define your columns in is important, and is the same order they are defined in on a model’s corresponding table.
Column Types¶
Column Options¶
Each column can be defined with optional arguments to modify the way they behave. While some column types may define additional column options, these are the options that are available on all columns:
primary_key
If True, this column is created as a primary key field. A model can have multiple primary keys. Defaults to False.
In CQL, there are 2 types of primary keys: partition keys and clustering keys. As with CQL, the first primary key is the partition key, and all others are clustering keys, unless partition keys are specified manually using
partition_key
partition_key
- If True, this column is created as partition primary key. There may be many partition keys defined, forming a composite partition key
clustering_order
ASC
orDESC
, determines the clustering order of a clustering key.index
- If True, an index will be created for this column. Defaults to False.
db_field
- Explicitly sets the name of the column in the database table. If this is left blank, the column name will be the same as the name of the column attribute. Defaults to None.
default
- The default value for this column. If a model instance is saved without a value for this column having been defined, the default value will be used. This can be either a value or a callable object (ie: datetime.now is a valid default argument). Callable defaults will be called each time a default is assigned to a None value
required
- If True, this model cannot be saved without a value defined for this column. Defaults to False. Primary key fields always require values.
static
- Defined a column as static. Static columns are shared by all rows in a partition.
Model Methods¶
Below are the methods that can be called on model instances.
-
class
cqlengine.models.
Model
(**values)¶ Creates an instance of the model. Pass in keyword arguments for columns you’ve defined on the model.
Example
#using the person model from earlier: class Person(Model): id = columns.UUID(primary_key=True) first_name = columns.Text() last_name = columns.Text() person = Person(first_name='Blake', last_name='Eggleston') person.first_name #returns 'Blake' person.last_name #returns 'Eggleston'
-
save
()¶ Saves an object to the database
Example
#create a person instance person = Person(first_name='Kimberly', last_name='Eggleston') #saves it to Cassandra person.save()
-
delete
()¶ Deletes the object from the database.
-
batch
(batch_object)¶ Sets the batch object to run instance updates and inserts queries with.
-
timestamp
(timedelta_or_datetime)¶ Sets the timestamp for the query
-
ttl
(ttl_in_sec)¶ Sets the ttl values to run instance updates and inserts queries with.
-
if_not_exists
()¶ Check the existence of an object before insertion. The existence of an object is determined by its primary key(s). And please note using this flag would incur performance cost.
if the insertion didn’t applied, a LWTException exception would be raised.
Example
This method is supported on Cassandra 2.0 or later.
-
iff
(**values)¶ Checks to ensure that the values specified are correct on the Cassandra cluster. Simply specify the column(s) and the expected value(s). As with if_not_exists, this incurs a performance cost.
If the insertion isn’t applied, a LWTException is raised
-
update
(**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 Blind Updates
-
get_changed_columns
()¶ Returns a list of column names that have changed since the model was instantiated or saved
-
Model Attributes¶
Model.
__abstract__
¶Optional. Indicates that this model is only intended to be used as a base class for other models. You can’t create tables for abstract models, but checks around schema validity are skipped during class construction.
Model.
__table_name__
¶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.
Model.
__keyspace__
¶Sets the name of the keyspace used by this model.
Prior to cqlengine 0.16, this setting defaulted to ‘cqlengine’. As of 0.16, this field needs to be set on all non-abstract models, or their base classes.
Model.
__default_ttl__
¶Sets the default ttl used by this model. This can be overridden by using the
ttl(ttl_in_sec)
method.
Table Polymorphism¶
As of cqlengine 0.8, it is possible to save and load different model classes using a single CQL table. This is useful in situations where you have different object types that you want to store in a single cassandra row.
For instance, suppose you want a table that stores rows of pets owned by an owner:
class Pet(Model): __table_name__ = 'pet' owner_id = UUID(primary_key=True) pet_id = UUID(primary_key=True) pet_type = Text(polymorphic_key=True) name = Text() def eat(self, food): pass def sleep(self, time): pass class Cat(Pet): __polymorphic_key__ = 'cat' cuteness = Float() def tear_up_couch(self): pass class Dog(Pet): __polymorphic_key__ = 'dog' fierceness = Float() def bark_all_night(self): passAfter calling
sync_table
on each of these tables, the columns defined in each model will be added to thepet
table. Additionally, savingCat
andDog
models will save the meta data needed to identify each row as either a cat or dog.To setup a polymorphic model structure, follow these steps
- Create a base model with a column set as the polymorphic_key (set
polymorphic_key=True
in the column definition)- Create subclass models, and define a unique
__polymorphic_key__
value on each- Run
sync_table
on each of the sub tablesAbout the polymorphic key
The polymorphic key is what cqlengine uses under the covers to map logical cql rows to the appropriate model type. The base model maintains a map of polymorphic keys to subclasses. When a polymorphic model is saved, this value is automatically saved into the polymorphic key column. You can set the polymorphic key column to any column type that you like, with the exception of container and counter columns, although
Integer
columns make the most sense. Additionally, if you setindex=True
on your polymorphic key column, you can execute queries against polymorphic subclasses, and aWHERE
clause will be automatically added to your query, returning only rows of that type. Note that you must define a unique__polymorphic_key__
value to each subclass, and that you can only assign a single polymorphic key column per model
Extending Model Validation¶
Each time you save a model instance in cqlengine, the data in the model is validated against the schema you’ve defined for your model. Most of the validation is fairly straightforward, it basically checks that you’re not trying to do something like save text into an integer column, and it enforces the
required
flag set on column definitions. It also performs any transformations needed to save the data properly.However, there are often additional constraints or transformations you want to impose on your data, beyond simply making sure that Cassandra won’t complain when you try to insert it. To define additional validation on a model, extend the model’s validation method:
class Member(Model): person_id = UUID(primary_key=True) name = Text(required=True) def validate(self): super(Member, self).validate() if self.name == 'jon': raise ValidationError('no jon\'s allowed')Note: while not required, the convention is to raise a
ValidationError
(from cqlengine import ValidationError
) if validation fails
Table Properties¶
Each table can have its own set of configuration options. These can be specified on a model with the following attributes:
Model.
__bloom_filter_fp_chance
¶
Model.
__caching__
¶
Model.
__comment__
¶
Model.
__dclocal_read_repair_chance__
¶
Model.
__default_time_to_live__
¶
Model.
__gc_grace_seconds__
¶
Model.
__index_interval__
¶
Model.
__memtable_flush_period_in_ms__
¶
Model.
__populate_io_cache_on_flush__
¶
Model.
__read_repair_chance__
¶
Model.
__replicate_on_write__
¶Example:
from cqlengine import CACHING_ROWS_ONLY, columns from cqlengine.models import Model class User(Model): __caching__ = CACHING_ROWS_ONLY # cache only rows instead of keys only by default __gc_grace_seconds__ = 86400 # 1 day instead of the default 10 days user_id = columns.UUID(primary_key=True) name = columns.Text()Will produce the following CQL statement:
CREATE TABLE cqlengine.user ( user_id uuid, name text, PRIMARY KEY (user_id) ) WITH caching = 'rows_only' AND gc_grace_seconds = 86400;See the list of supported table properties for more information.
Compaction Options¶
As of cqlengine 0.7 we’ve added support for specifying compaction options. cqlengine will only use your compaction options if you have a strategy set. When a table is synced, it will be altered to match the compaction options set on your table. This means that if you are changing settings manually they will be changed back on resync. Do not use the compaction settings of cqlengine if you want to manage your compaction settings manually.
cqlengine supports all compaction options as of Cassandra 1.2.8.
Available Options:
Model.
__compaction_bucket_high__
¶
Model.
__compaction_bucket_low__
¶
Model.
__compaction_max_compaction_threshold__
¶
Model.
__compaction_min_compaction_threshold__
¶
Model.
__compaction_min_sstable_size__
¶
Model.
__compaction_sstable_size_in_mb__
¶
Model.
__compaction_tombstone_compaction_interval__
¶
Model.
__compaction_tombstone_threshold__
¶For example:
class User(Model): __compaction__ = cqlengine.LeveledCompactionStrategy __compaction_sstable_size_in_mb__ = 64 __compaction_tombstone_threshold__ = .2 user_id = columns.UUID(primary_key=True) name = columns.Text()or for SizeTieredCompaction:
class TimeData(Model): __compaction__ = SizeTieredCompactionStrategy __compaction_bucket_low__ = .3 __compaction_bucket_high__ = 2 __compaction_min_threshold__ = 2 __compaction_max_threshold__ = 64 __compaction_tombstone_compaction_interval__ = 86400Tables may use LeveledCompactionStrategy or SizeTieredCompactionStrategy. Both options are available in the top level cqlengine module. To reiterate, you will need to set your __compaction__ option explicitly in order for cqlengine to handle any of your settings.
Manipulating model instances as dictionaries¶
As of cqlengine 0.12, we’ve added support for treating model instances like dictionaries. See below for examples.
class Person(Model): first_name = columns.Text() last_name = columns.Text() kevin = Person.create(first_name="Kevin", last_name="Deldycke") dict(kevin) # returns {'first_name': 'Kevin', 'last_name': 'Deldycke'} kevin['first_name'] # returns 'Kevin' kevin.keys() # returns ['first_name', 'last_name'] kevin.values() # returns ['Kevin', 'Deldycke'] kevin.items() # returns [('first_name', 'Kevin'), ('last_name', 'Deldycke')] kevin['first_name'] = 'KEVIN5000' # changes the models first name