【前往Cassandra Day的路上】使用对象映射进行数据库编程(Python)

首先

东京的卡桑德拉日

今年的6月1日,Cassandra Day将在日本举办。去年,Cassandra Day已经在柏林、伦敦、阿姆斯特丹、河内、雅加达、休斯顿、圣克拉拉、西雅图和新加坡成功举办过。

这次在东京举办活动,我们将发布与Apache Cassandra相关的文章。

image.png

关于Apache Cassandra

Apache Cassandra是一个开源的分布式数据库管理系统。

与其他分布式数据库管理系统相同,我们将使用多个通用服务器来构建一个数据库(为了开发等目的,也可以只使用一个服务器来构建)。

在这里,我们将省略详细的说明,将介绍感兴趣的人的角色委托给官方网站或维基百科。

 

通过对象映射来进行数据库编程

数据库和对象映射

在数据库中管理的数据(记录)和面向对象编程中使用的数据(对象)之间的转换通常需要掌握专用库,尤其是在关系型数据库中。这样的功能被称为对象(关系)映射。

Cassandra与对象映射

在Cassandra中,对象映射功能由cassandra.cqlengine包提供。

本文将介绍如何利用该程序包提供的对象映射功能进行编程的概要。

 

关于处理Cassandra数据模型的详细信息,请参考上述文档。然而,Cassandra使用CQL(Cassandra Query Language)协议进行本地访问,如果你有关系型数据库的经验,你应该可以几乎完全理解下面的代码(如果出现不熟悉的术语如”聚类键”,请适时查阅相关文档。不过,应该没有问题来掌握整体的概念)。

示例程序

首先,我们将展示示例程序的整体结构。

import uuid
from cassandra.cqlengine import columns
from cassandra.cqlengine import connection
from datetime import datetime
from cassandra.cqlengine.management import sync_table
from cassandra.cqlengine.models import Model

# まず、モデルを定義します。
class ExampleModel(Model):
    example_id      = columns.UUID(primary_key=True, default=uuid.uuid4)
    example_type    = columns.Integer(index=True)
    created_at      = columns.DateTime()
    description     = columns.Text(required=False)

# 次にCassandraデータベースとの接続を行います。
# 利用できるオプションについては、次を参照: http://datastax.github.io/python-driver/api/cassandra/cluster.html
# 配列として、接続先ホストのリストをClusterインスタンス作成時に渡します(ここでは開発環境を想定し、ローカルホスト一つを利用)。
connection.setup(['127.0.0.1'], "cqlengine", protocol_version=3)

以下是在Python命令行界面上执行并输出结果的示例图像。

# 以下の操作により、定義したモデルのテーブルがCassandraに作成されます。
>>> sync_table(ExampleModel)

# これでテーブルにレコード(列)を追加することができます。
>>> em1 = ExampleModel.create(example_type=0, description="example1", created_at=datetime.now())
>>> em2 = ExampleModel.create(example_type=0, description="example2", created_at=datetime.now())
>>> em3 = ExampleModel.create(example_type=0, description="example3", created_at=datetime.now())
>>> em4 = ExampleModel.create(example_type=0, description="example4", created_at=datetime.now())
>>> em5 = ExampleModel.create(example_type=1, description="example5", created_at=datetime.now())
>>> em6 = ExampleModel.create(example_type=1, description="example6", created_at=datetime.now())
>>> em7 = ExampleModel.create(example_type=1, description="example7", created_at=datetime.now())
>>> em8 = ExampleModel.create(example_type=1, description="example8", created_at=datetime.now())

# テーブルに対してクエリを実行することもできます。
>>> ExampleModel.objects.count()
8
>>> q = ExampleModel.objects(example_type=1)
>>> q.count()
4
>>> for instance in q:
>>>     print instance.description
example5
example6
example7
example8

# ここでは、追加のフィルターを適用します。
# queryオブジェクトはイミュータブルです。つまり、ここで戻されるオブジェクト(q2)は、新しいオブジェクトです。
>>> q2 = q.filter(example_id=em5.example_id)

>>> q2.count()
1
>>> for instance in q2:
>>>     print instance.description
example5

模型概要

模型是表示CQL表格的Python类。模型是从Model派生出来的,并定义了基本表格属性和表格列。

模型的列会映射到CQL表的列上。通过在模型类中定义列的属性,可以定义CQL列。为了使模型有效,至少需要一个主键列和一个非主键列。与CQL类似,定义列的顺序也很重要。这与在对应的表中定义列的顺序相同。

以下是定义模型的基本示例。有关详细信息,请参阅文档。

桥本是模特的典范。

在这个示例中,我们定义了一个继承自Model类并具有列first_name和last_name的Person类。

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()

此”Person”类对应以下CQL表。可以使用”create table”语句来表示。

CREATE TABLE cqlengine.person (
    id uuid,
    first_name text,
    last_name text,
    PRIMARY KEY (id)
);

接下来,我们将介绍另一种模型定义方法的例子,即Comment类。

在这里,作为一个新的要素,我们可以利用clustering_order=”DESC”这个降序(DESC)的聚类键。

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()

上述的 Comment 模型对应的表格如下所示。通过 create table 语句创建。

CREATE TABLE comment (
  photo_id uuid,
  comment_id timeuuid,
  comment text,
  PRIMARY KEY (photo_id, comment_id)
) WITH CLUSTERING ORDER BY (comment_id DESC);

同步这些模型到数据库,按照以下步骤进行操作。(注意,在此处,Person和Comment不是对象,而是类)

from cassandra.cqlengine.management import sync_table
sync_table(Person)
sync_table(Comment)

模型同步需要进行模式更改。必须考虑对数据库的影响并进行操作。有关注意事项,请参阅文档中的模式管理说明。

模型操作

模型实例可以通过类似字典操作的方式进行访问,具体如下。

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

模型验证的扩展

每次将模型实例保存到cqlengine中时,模型内的数据将根据为模型定义的架构进行验证。这种验证通常是显而易见的。例如,它会确保不会混淆不同的数据类型,也就是说,不会将文本保存为整数列。此外,它还会执行必要的转换以正确保存数据。

然而,除了确保在尝试插入数据时不会在Cassandra数据库中出现错误之外,还可能希望添加额外的约束和转换。要定义额外的验证逻辑以验证模型,需要扩展该模型的validate方法。

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')

如果验证失败,可以引发(cassandra.cqlengine.) ValidationError。然而,这并非必需。也可以考虑将其转换为失败验证的默认值等其他处理方法。

模型的继承

使用单个CQL表格,您可以保存和加载不同的模型类。这对于希望将不同对象类型存储在单个Cassandra行中的情况非常有用。

假设有一个存储猫和狗相关信息的宠物表格。

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

在调用sync_table之后,每个模型定义的列将被添加到pet表中。此外,保存模型的Cat和Dog行所需的元数据将被保存,以便识别它们是猫还是狗。

要使用继承来设置模型结构,请按照以下步骤进行操作。

    • 識別子(ここではpet_type)として設定された列を持つ基本モデルを作成する(distriminator_column=Trueにより指定)。

 

    • サブクラス モデルを(複数)定義し、__discriminator_value__フィールドに(それぞれ)一意の値を定義する

sync_tableを各サブテーブルで実行する

自定义类型

cqlengine可以对用户定义类型(UDT:User Defined Type)进行建模。
UDT可以作为模型(表)的字段被使用,而不是单独使用。
UDT实例通过对应于表的模型进行创建、保存和查询。

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)

users.create(name="Joe", addr=address(street="Easy St.", zipcode=99999))
user = users.objects(name="Joe")[0]
print user.name, user.addr
# Joe address(street=u'Easy St.', zipcode=99999)

UDT被定义为继承UserType的类,并通过为其属性字段设置相应数据类型的实例来进行建模。
然后,通过将UDT类声明为UserDefinedType类的参数,可以在模型的(字段/行)定义中使用UDT。

“Sync_table将隐式同步包含在表中的所有UDT。或者,您可以使用sync_type()来显式地创建/修改UDT。”

声明后,类型将自动注册给驱动程序,因此查询结果将返回UserType类的实例。

最终

本次介绍了在使用Python进行Cassandra编程时,对象映射的工作原理。当然,也可以不使用对象映射,而是使用CQL查询来进行编程。在项目中使用Cassandra时,介绍了对象映射作为一种可选的选择。

广告
将在 10 秒后关闭
bannerAds