221 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			221 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
======
 | 
						|
Models
 | 
						|
======
 | 
						|
 | 
						|
.. module:: cqlengine.models
 | 
						|
 | 
						|
A model is a python class representing a CQL table. Models derive from :class:`Model`, and
 | 
						|
define basic table properties and columns for a table.
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
Some basic examples defining models are shown below. Consult the :doc:`Model API docs </api/cassandra/cqlengine/models>` and :doc:`Column API docs </api/cassandra/cqlengine/columns>` for complete details.
 | 
						|
 | 
						|
Example Definitions
 | 
						|
===================
 | 
						|
 | 
						|
This example defines a ``Person`` table, with the columns ``first_name`` and ``last_name``
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
   from cassandra.cqlengine import columns
 | 
						|
   from cassandra.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:
 | 
						|
 | 
						|
.. code-block:: sql
 | 
						|
 | 
						|
   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:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from cassandra.cqlengine import columns
 | 
						|
    from cassandra.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:
 | 
						|
 | 
						|
.. code-block:: sql
 | 
						|
 | 
						|
    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*:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from cassandra.cqlengine.management import sync_table
 | 
						|
    sync_table(Person)
 | 
						|
    sync_table(Comment)
 | 
						|
 | 
						|
\*Note: synchronizing models causes schema changes, and should be done with caution.
 | 
						|
Please see the discussion in :doc:`/api/cassandra/cqlengine/management` for considerations.
 | 
						|
 | 
						|
For examples on manipulating data and creating queries, see :doc:`queryset`
 | 
						|
 | 
						|
Manipulating model instances as dictionaries
 | 
						|
============================================
 | 
						|
 | 
						|
Model instances can be accessed like dictionaries.
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    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
 | 
						|
 | 
						|
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:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    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.
 | 
						|
 | 
						|
.. _model_inheritance:
 | 
						|
 | 
						|
Model Inheritance
 | 
						|
=================
 | 
						|
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:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    class Pet(Model):
 | 
						|
        __table_name__ = 'pet'
 | 
						|
        owner_id = UUID(primary_key=True)
 | 
						|
        pet_id = UUID(primary_key=True)
 | 
						|
        pet_type = Text(discriminator_column=True)
 | 
						|
        name = Text()
 | 
						|
 | 
						|
        def eat(self, food):
 | 
						|
            pass
 | 
						|
 | 
						|
        def sleep(self, time):
 | 
						|
            pass
 | 
						|
 | 
						|
    class Cat(Pet):
 | 
						|
        __discriminator_value__ = 'cat'
 | 
						|
        cuteness = Float()
 | 
						|
 | 
						|
        def tear_up_couch(self):
 | 
						|
            pass
 | 
						|
 | 
						|
    class Dog(Pet):
 | 
						|
        __discriminator_value__ = 'dog'
 | 
						|
        fierceness = Float()
 | 
						|
 | 
						|
        def bark_all_night(self):
 | 
						|
            pass
 | 
						|
 | 
						|
After calling ``sync_table`` on each of these tables, the columns defined in each model will be added to the
 | 
						|
``pet`` table. Additionally, saving ``Cat`` and ``Dog`` models will save the meta data needed to identify each row
 | 
						|
as either a cat or dog.
 | 
						|
 | 
						|
To setup a model structure with inheritance, follow these steps
 | 
						|
 | 
						|
1.  Create a base model with a column set as the distriminator (``distriminator_column=True`` in the column definition)
 | 
						|
2.  Create subclass models, and define a unique ``__discriminator_value__`` value on each
 | 
						|
3.  Run ``sync_table`` on each of the sub tables
 | 
						|
 | 
						|
**About the discriminator value**
 | 
						|
 | 
						|
The discriminator value is what cqlengine uses under the covers to map logical cql rows to the appropriate model type. The
 | 
						|
base model maintains a map of discriminator values to subclasses. When a specialized model is saved, its discriminator value is
 | 
						|
automatically saved into the discriminator column. The discriminator column may be any column type except counter and container types.
 | 
						|
Additionally, if you set ``index=True`` on your discriminator column, you can execute queries against specialized subclasses, and a
 | 
						|
``WHERE`` clause will be automatically added to your query, returning only rows of that type. Note that you must
 | 
						|
define a unique ``__discriminator_value__`` to each subclass, and that you can only assign a single discriminator column per model.
 | 
						|
 | 
						|
.. _user_types:
 | 
						|
 | 
						|
User Defined Types
 | 
						|
==================
 | 
						|
cqlengine models User Defined Types (UDTs) much like tables, with fields defined by column type attributes. However, UDT instances
 | 
						|
are only created, presisted, and queried via table Models. A short example to introduce the pattern::
 | 
						|
 | 
						|
    from cassandra.cqlengine.columns import *
 | 
						|
    from cassandra.cqlengine.models import Model
 | 
						|
    from cassandra.cqlengine.usertype import UserType
 | 
						|
 | 
						|
    class address(UserType):
 | 
						|
        street = Text()
 | 
						|
        zipcode = Integer()
 | 
						|
 | 
						|
    class users(Model):
 | 
						|
        __keyspace__ = 'account'
 | 
						|
        name = Text(primary_key=True)
 | 
						|
        addr = UserDefinedType(address)
 | 
						|
 | 
						|
    sync_table(users)
 | 
						|
 | 
						|
    users.create(name="Joe", addr=address(street="Easy St.", zip=99999))
 | 
						|
    user = users.objects(name="Joe")[0]
 | 
						|
    print user.name, user.addr
 | 
						|
    # Joe {'street': Easy St., 'zipcode': None}
 | 
						|
 | 
						|
UDTs are modeled by inheriting :class:`~.usertype.UserType`, and setting column type attributes. Types are then used in defining
 | 
						|
models by declaring a column of type :class:`~.columns.UserDefinedType`, with the ``UserType`` class as a parameter.
 | 
						|
 | 
						|
``sync_table`` will implicitly
 | 
						|
synchronize any types contained in the table. Alternatively :func:`~.management.sync_type` can be used to create/alter types
 | 
						|
explicitly.
 | 
						|
 | 
						|
Upon declaration, types are automatically registered with the driver, so query results return instances of your ``UserType``
 | 
						|
class*.
 | 
						|
 | 
						|
***Note**: UDTs were not added to the native protocol until v3. When setting up the cqlengine connection, be sure to specify
 | 
						|
``protocol_version=3``. If using an earlier version, UDT queries will still work, but the returned type will be a namedtuple.
 |