From 11904ed9c329810538bc741395d9ad9ad333f686 Mon Sep 17 00:00:00 2001 From: Tyler Hobbs Date: Tue, 5 Aug 2014 14:31:03 -0500 Subject: [PATCH] Add docs section for UDTs Fixes PYTHON-114 --- docs/index.rst | 4 ++ docs/upgrading.rst | 2 + docs/user_defined_types.rst | 90 +++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 docs/user_defined_types.rst diff --git a/docs/index.rst b/docs/index.rst index 12180f00..8763baba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -30,6 +30,9 @@ Contents :doc:`query_paging` Notes on paging large query results. +:doc:`user_defined_types` + Working with Cassandra 2.1's user-defined types. + :doc:`security` An overview of the security features of the driver. @@ -58,6 +61,7 @@ If you would like to contribute, please feel free to open a pull request. performance query_paging security + user_defined_types Indices and Tables ================== diff --git a/docs/upgrading.rst b/docs/upgrading.rst index 4102a39e..bb0aad5a 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -63,6 +63,8 @@ If no class is registered for a user-defined type, query results will use a ``namedtuple`` class and data may only be inserted though prepared statements. +See :ref:`udts` for more details. + Customizing Encoders for Non-prepared Statements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Starting with version 2.1 of the driver, it is possible to customize diff --git a/docs/user_defined_types.rst b/docs/user_defined_types.rst new file mode 100644 index 00000000..ed915f24 --- /dev/null +++ b/docs/user_defined_types.rst @@ -0,0 +1,90 @@ +.. _udts: + +User Defined Types +================== +Cassandra 2.1 introduced user-defined types (UDTs). You can create a +new type through ``CREATE TYPE`` statements in CQL:: + + CREATE TYPE address (street text, zip int); + +Version 2.1 of the python driver adds support for user-defined types. + +Registering a Class to Map to a UDT +----------------------------------- +You can tell the python driver to return columns of a specific UDT as +instances of a class by registering them with your :class:`~.Cluster` +instance through :meth:`.Cluster.register_user_type`: + +.. code-block:: python + + cluster = Cluster(protocol_version=3) + session = cluster.connect() + session.set_keyspace('mykeyspace') + session.execute("CREATE TYPE address (street text, zipcode int)") + session.execute("CREATE TABLE users (id int PRIMARY KEY, location address)") + + # create a class to map to the "address" UDT + class Address(object): + + def __init__(self, street, zipcode): + self.street = street + self.zipcode = zipcode + + cluster.register_user_type('mykeyspace', 'address', Address) + + # insert a row using an instance of Address + session.execute("INSERT INTO users (id, location) VALUES (%s, %s)", + (0, Address("123 Main St.", 78723))) + + # results will include Address instances + results = session.execute("SELECT * FROM users") + row = results[0] + print row.id, row.location.street, row.location.zipcode + +Using UDTs Without Registering Them +----------------------------------- +Although it is recommended to register your types with +:meth:`.Cluster.register_user_type`, the driver gives you some options +for working with unregistered UDTS. + +When you use prepared statements, the driver knows what data types to +expect for each placeholder. This allows you to pass any object you +want for a UDT, as long as it has attributes that match the field names +for the UDT: + +.. code-block:: python + + cluster = Cluster(protocol_version=3) + session = cluster.connect() + session.set_keyspace('mykeyspace') + session.execute("CREATE TYPE address (street text, zipcode int)") + session.execute("CREATE TABLE users (id int PRIMARY KEY, location address)") + + class Foo(object): + + def __init__(self, street, zipcode, otherstuff): + self.street = street + self.zipcode = zipcode + self.otherstuff = otherstuff + + insert_statement = session.prepare("INSERT INTO users (id, location) VALUES (?, ?)") + + # since we're using a prepared statement, we don't *have* to register + # a class to map to the UDT to insert data. The object just needs to have + # "street" and "zipcode" attributes (which Foo does): + session.execute(insert_statement, [0, Foo("123 Main St.", 78723, "some other stuff")] + + # when we query data, UDT columns that don't have a class registered + # will be returned as namedtuples: + results = session.execute("SELECT * FROM users") + first_row = results[0] + address = first_row.address + print address # prints "Address(street='123 Main St.', zipcode=78723)" + street = address.street + zipcode = address.street + +As shown in the code example, inserting data for UDT columns without registering +a class works fine for prepared statements. However, **you must register a +class to insert UDT columns with unprepared statements**. You can still query +UDT columns without registered classes using unprepared statements, they will +simply return ``namedtuple`` instances (just like prepared statements do).