まくろぐ

Azure Table Strage を使ってみる (3) Python からテーブル操作してみる

更新:
作成:

azure-cosmosdb-table パッケージのインストール

Python から Azure Table Storage を操作するには、azure-cosmosdb-table というライブラリを使用します。

azure-cosmosdb-table のインストール
$ pip install azure-cosmosdb-table
☝️ ワンポイント

Cosmos DB をまったく使わない場合でも cosmosdb と名前のついたライブラリを使わせようとするのは、 Microsoft の策略 Cosmos DB に力を入れているという意思表示でしょう。 正直なところ Cosmos DB はお金がかかりすぎて個人の趣味レベルでは使えないのですが。。。

(追記: 2021年)CosmosDB に無料枠ができて少しずつ個人利用もできそうな感じになってきました。

TableService オブジェクトの生成

Azure Storage にアクセスするには、接続情報(ストレージアカウント名とキー)が必要になるので、Azure ポータル で確認しておいてください。

Table Storage にアクセスしてごにょごにょするには、TableService クラス のメソッドを使用します。 TableService のコンストラクタには、ストレージアカウント名とキーを渡します。

from azure.cosmosdb.table.tableservice import TableService

STORAGE_NAME = 'maku77storage'
STORAGE_KEY = 'XlgKN4Hv...(省略)...F55o3N9g=='

table_service = TableService(account_name=STORAGE_NAME, account_key=STORAGE_KEY)
☝️ URL の指定は必要ない? 接続先アドレス (URL) の構築は TableService クラスがよろしくやってくれるので、実装コードがとてもスッキリします。 ストレージアカウント名は Azure 内(世界中)で一意になっているので、ストレージアカウント名さえ指定すれば、Web API の URL も自動的に決まるということですね。

ストレージアカウントのキーを環境変数から取得する

ストレージアカウントにアクセスするためのキー情報をスクリプト内にハードコーディングするのは望ましくないので、環境変数などから取得するようにしておくとよいでしょう。

環境変数から Azure Storage のアクセスキーを取得
import os
import sys

if 'AZURE_STORAGE_KEY' not in os.environ:
    print('Error: AZURE_STORAGE_KEY not found', file=sys.stderr)
    sys.exit(1)
storage_key = os.getenv('AZURE_STORAGE_KEY')

プロキシ環境からアクセスする場合

TableService クラスをプロキシ環境内で使用する場合は、OS の https_proxy 環境変数で設定してしまうのが簡単です。 OS の https_proxy 環境変数を設定するのを避けたいときは、Python スクリプトの中からプロキシを設定してしまうこともできます。

プロキシ情報をハードコードする
import os
os.environ['https_proxy'] = 'http://proxy.example.com:12345'

OS の https_proxy 環境変数を設定するのも嫌だし、Python スクリプト内にプロキシ情報をハードコードするのも嫌なときは、例えば、独自の AZURE_PROXY のような 独自の環境変数 を用意して、その値をプロキシ情報として使用するという方法があります。

AZURE_PROXY 環境変数の値をプロキシ情報として使う
if 'AZURE_PROXY' in os.environ:
    os.environ['https_proxy'] = os.getenv('AZURE_PROXY')

テーブルの操作

ここから先のテーブル操作に関しては、前述の TableService オブジェクトの生成が終わっていることを想定しています。

テーブルのリストを取得する (list_tables)

Table Storage 上に存在するテーブルの一覧を取得するには、TableService クラスの list_tables() メソッド を使用します。 このメソッドの戻り値を for ループで処理すると、azure.cosmosdb.table.models.Table オブジェクトを 1 つずつ取り出すことができます。 テーブル名を示す name プロパティしかありませんが。。。

すべてのテーブルを列挙
for table in table_service.list_tables():
    print(table.name)

仮に、bookstodos というテーブルが作成済みであれば、次のように表示されます。

実行結果
books
todos

テーブルの存在を確認する (exist)

Table Storage 上に、指定した名前のテーブルが存在するかどうかをチェックするには、TableService クラスの exists() メソッド を使用します。

if table_service.exists('books'):
    print('books テーブルが存在するよ')

テーブルを作成する (create_table)

Table Storage 上に、新しいテーブルを作成するには、TableService クラスの create_table() メソッド を使用します。 テーブルの作成に成功すると True を返します。

result = table_service.create_table('mytable')
if result:
    print('テーブルを作成しました')
☝️ ワンポイント テーブルのプロパティ(RDB のカラムに相当するもの)の指定がありませんが、プロパティはエンティティを追加するときに自動的に生成されるので、ここで指定しておく必要はありません。

テーブルを削除する (delete_table)

Table Storage 上の、既存のテーブルを削除するには、TableService クラスの delete_table() メソッド を使用します。 テーブルを削除すると、そこに含まれているすべてのエンティティも削除されてしまうので注意してください。

table_service.delete_table('mytable')

エンティティの操作

テーブルにエンティティを追加する (insert_xxx)

指定したテーブルにデータ(エンティティ)を追加するには、TableService クラスの下記のいずれかのメソッドを使用します。 同じキー情報を持つエンティティを追加しようとしたときの振る舞いだけが異なります。

insert_entity()
テーブル内に既に同じ PartitionKeyRowKey を持つエンティティがある場合は、例外を発生します。
insert_or_merge_entity()
テーブル内に既に同じ PartitionKeyRowKey を持つエンティティがある場合は、指定したプロパティだけ上書きされます(既存エンティティのプロパティとマージされる)。
insert_or_replace_entity()
テーブル内に既に同じ ParitionKeyRowKey を持つエンティティがある場合は、指定したエンティティに置き換えられます。

これらのメソッドのパラメータはすべて同じで、追加先のテーブル名と、追加するエンティティを渡します。

insert_entity(table_name, entity, timeout=None)
insert_or_merge_entity(table_name, entity, timeout=None)
insert_or_replace_entity(table_name, entity, timeout=None)

entity パラメータには、辞書オブジェクトか Entity クラス のオブジェクトを渡すことができます。

辞書でデータを指定する方法
book = {
    'PartitionKey': 'book',  # 必須のキー情報
    'RowKey': '0004',        # 必須のキー情報
    'Title': '雪男の秘密',
    'Author': '雪男'
}
table_service.insert_or_replace_entity('books', book)
Entity オブジェクトでデータを指定する方法
from azure.cosmosdb.table.models import Entity

book = Entity()
book.PartitionKey = 'book'  # 必須のキー情報
book.RowKey = '0004'        # 必須のキー情報
book.Title = '雪男の秘密'
book.Author = '雪男'
table_service.insert_or_replace_entity('books', book)

別に Entity オブジェクトを使ったところで型安全になるわけでもないので、辞書オブジェクトをそのまま使った方が楽かもしれません。

☝️ 存在しないプロパティは自動で追加される insert 系メソッドでエンティティを追加するときに、テーブルにまだ存在しないプロパティを指定することもできます。 その場合、自動的にテーブルにそのプロパティが追加されるので、プロパティ名は間違えないように要注意です。

指定したキーのエンティティを取得する (get_entity)

取得したいエンティティのキー(PartitionKeyRowKey)が分かっている場合は、TableService クラスの get_entity() メソッド を使って Entity オブジェクトを取得することができます。 指定したキーのエンティティが見つからない場合は、例外が発生します。

book = table_service.get_entity(
    table_name='books',
    partition_key='book',
    row_key='0004')

print(book.Title)
print(book.Author)

特定のプロパティしか参照しないことが分かっている場合は、select パラメータでプロパティ名を列挙しておくと、通信量を節約することができます。

book = table_service.get_entity(
    table_name='books',
    partition_key='book',
    row_key='0004',
    select='Title,Author')  # Title と Author プロパティだけ取得

指定したキーのエンティティを削除する (delete_entity)

指定したキー(PartitionKeyRowKey)のエンティティを削除するには、TableService クラスの delete_entity() メソッド を使用します。 指定したキーのエンティティが見つからない場合は、例外が発生します。

table_service.delete_entity(
    table_name='books',
    partition_key='book',
    row_key='0004')

エンティティのリストを取得する (query_entities)

テーブルから条件に一致するエンティティをすべて取得するには、TableService クラスの query_entities() メソッド を使用します。

条件なしで検索
books = table_service.query_entities(
    table_name='books',
    num_results=5)

for book in books:
    print(book.Title, book.Author)

検索条件は、filter パラメータを使って指定することができます。filter パラメータには文字列でフィルタ条件を指定するのですが、どのようなフォーマットで指定すればよいかは、下記のサイトが参考になります。

例: PartitionKey が Python である

filter パラメータに ParitionKey eq 'Python' と指定すると、PartitionKeyPython であるエンティティをすべて取得することができます。

python_books = table_service.query_entities(
    'books', filter="PartitionKey eq 'Python'")

for book in python_books:
    print(book.Title, book.Author)

例: RowKey が 0002 より大きい

filter パラメータに RowKey gt '0002' と指定すると、RowKey0002 よりも大きいエンティティのみを取得することができます。

filter 条件を指定して検索
books = table_service.query_entities(
    table_name='books',
    filter="RowKey gt '0002'")

for book in books:
    print(book.Title, book.Author)
へむの秘密 へむ
ちいの秘密 ちい
雪男の秘密 雪男

バッチ処理化

複数のエンティティを追加したい場合などは、バッチ処理で行うようにすると、サーバーとの通信回数を減らすことができます。 バッチ処理を行うには、TableService クラスの batch() メソッド を使用します。

book1 = {'PartitionKey': 'book', 'RowKey': '001', Title: 'Title1'}
book2 = {'PartitionKey': 'book', 'RowKey': '002', Title: 'Title2'}
book3 = {'PartitionKey': 'book', 'RowKey': '003', Title: 'Title3'}

with table_service.batch('books') as batch:
    batch.insert_entity(book1)
    batch.insert_entity(book2)
    batch.insert_entity(book3)

関連記事

まくろぐ
サイトマップまくへのメッセージ