Python での Cloud Datastore の使用

Bookshelf チュートリアルのこのページでは、サンプルアプリが書籍のメタデータである永続データを Google Cloud Datastore に格納する仕組みを説明します。このステップのサンプルコードは、Cloud Datastore に保存されたデータを作成、読み取り、更新、削除(CRUD)する方法の例を示しています。

このページは複数ページからなるチュートリアルの一部です。最初からの説明や設定手順を確認するには、Python Bookshelf アプリに移動してください。

設定の構成

ここでは、2-structured-data ディレクトリにあるコードを使用します。ファイルを編集し、このディレクトリでコマンドを実行します。

  1. config.py を編集用に開きます。
  2. PROJECT_ID の値を、GCP Console に表示されるプロジェクト ID に設定します。
  3. DATA_BACKEND の値を datastore に設定します。
  4. config.py を保存して閉じます。
Cloud Datastore は、自動的に初期化されて App Engine アプリに接続されるフルマネージド サービスです。プロジェクトで初めて Cloud Datastore を使用する場合は、GCP Console で [Datastore] ページを開き、[Datastore モード] を選択してサービスを初期化する必要があります。そのため、これ以上の構成は不要です。

依存関係のインストール

以下のコマンドを入力して仮想環境を作成し、依存関係をインストールします。

Linux / macOS

virtualenv -p python3 env
source env/bin/activate
pip install -r requirements.txt

Windows

virtualenv -p python3 env
env\scripts\activate
pip install -r requirements.txt

ローカルマシンでのアプリの実行

  1. ローカル ウェブサーバーを起動します。

    python main.py
    
  2. ウェブブラウザに次のアドレスを入力します。

    http://localhost:8080

これで、アプリのウェブページを閲覧し、書籍の追加、編集、削除ができます。

Ctrl+C キーを押してワーカーを終了し、次にローカル ウェブサーバーを終了します。

App Engine フレキシブル環境へのアプリのデプロイ

  1. サンプルアプリをデプロイします。

    gcloud app deploy
    
  2. ウェブブラウザで、次のアドレスを入力します。[YOUR_PROJECT_ID] は実際のプロジェクト ID で置き換えます。

    https://[YOUR_PROJECT_ID].appspot.com
    

アプリを更新する場合は、最初にデプロイしたときと同じコマンドを使って、更新バージョンをデプロイできます。新たにデプロイすると、アプリの新しいバージョンが作成され、それがデフォルトのバージョンに設定されます。古いバージョンはそのまま残り、関連付けられた VM インスタンスも同様に残ります。すべてのアプリ バージョンと VM インスタンスが課金対象のリソースとなるのでご注意ください。

アプリのデフォルト以外のバージョンを削除することで、コストを削減できます。

アプリのバージョンを削除するには:

  1. GCP Console で、App Engine の [バージョン] ページに移動します。

    [バージョン] ページに移動

  2. デフォルト以外で削除するアプリのバージョンのチェックボックスを選択します。
  3. アプリのバージョンを削除するには、[削除]()をクリックします。

課金対象のリソースをクリーンアップする方法の詳細については、このチュートリアルの最後のステップにあるクリーンアップ セクションをご覧ください。

アプリケーションの構造

Bookshelf アプリのデプロイ プロセスと構造

アプリケーションは、すべての永続データを Cloud Datastore に格納します。

コードについて

このセクションではアプリケーションのコードとその動作を、順を追って説明します。

フォームを使用してユーザー入力の送信を処理する

追加 / 編集 HTML フォームを使用すると、アプリ内で書籍情報の送信内容を追加、編集できます。

追加 / 編集フォームの画像

HTML フォームは、Flask のデフォルトのテンプレート エンジン Jinja2 を使用して作成されます。次のテンプレートは、タイトル、著者名、出版日、説明を含む簡単なフォームを定義します。

{% extends "base.html" %}

{% block content %}
<h3>{{action}} book</h3>

<form method="POST" enctype="multipart/form-data">

  <div class="form-group">
    <label for="title">Title</label>
    <input type="text" name="title" id="title" value="{{book.title}}" class="form-control"/>
  </div>

  <div class="form-group">
    <label for="author">Author</label>
    <input type="text" name="author" id="author" value="{{book.author}}" class="form-control"/>
  </div>

  <div class="form-group">
    <label for="publishedDate">Date Published</label>
    <input type="text" name="publishedDate" id="publishedDate" value="{{book.publishedDate}}" class="form-control"/>
  </div>

  <div class="form-group">
    <label for="description">Description</label>
    <textarea name="description" id="description" class="form-control">{{book.description}}</textarea>
  </div>

  <button type="submit" class="btn btn-success">Save</button>
</form>

{% endblock %}

フォーム送信を処理する

ユーザーが [Add Book] をクリックすると、crud.add ビューにフォームが表示されます。ユーザーが [Add book] フォームに入力して [Save] をクリックすると、同じビューでフォームの HTTP POST アクションが処理されます。これにより、データが get_model().create 関数に渡され、Cloud Database へのデータ送信プロセスが開始されます。

@crud.route('/add', methods=['GET', 'POST'])
def add():
    if request.method == 'POST':
        data = request.form.to_dict(flat=True)

        book = get_model().create(data)

        return redirect(url_for('.view', id=book['id']))

    return render_template("form.html", action="Add", book={})

bookshelf/model_datastore.py ファイルには、Cloud Datastore に保存されたデータの CRUD 関数を実行するコードが含まれています。たとえば、get_model().create ステートメントにより bookshelf/model_datastore.py 内の create 関数が呼び出され、この関数はユーザーが送信したデータを update 関数に送ります。最初のパラメータで null 値が渡され、これが新しい書籍情報の送信であることを示します。ユーザーが送信したデータを実際に Cloud Datastore に保存する update 関数は、次のとおりです。

def update(data, id=None):
    ds = get_client()
    if id:
        key = ds.key('Book', int(id))
    else:
        key = ds.key('Book')

    entity = datastore.Entity(
        key=key,
        exclude_from_indexes=['description'])

    entity.update(data)
    ds.put(entity)
    return from_datastore(entity)

create = update

get_client ヘルパー関数が、Cloud Datastore API とやり取りするクライアントを作成します。

def get_client():
    return datastore.Client(current_app.config['PROJECT_ID'])

from_datastore ヘルパー関数は、データストアのエンティティ キーをアプリケーションで使用する ID に変換するために使用されます。

def from_datastore(entity):
    """Translates Datastore results into the format expected by the
    application.

    Datastore typically returns:
        [Entity{key: (kind, id), prop: val, ...}]

    This returns:
        {id: id, prop: val, ...}
    """
    if not entity:
        return None
    if isinstance(entity, builtin_list):
        entity = entity.pop()

    entity['id'] = entity.key.id
    return entity

ユーザーが書籍を追加した後に [Books] リンクをクリックすると、Cloud Datastore に現在保存されているすべての書籍を一覧表示する /books ページに移動します。model_datastore.list 関数は、Cloud Datastore から取得したデータを使用して、すべての書籍を一覧表示します。

def list(limit=10, cursor=None):
    ds = get_client()

    query = ds.query(kind='Book', order=['title'])
    query_iterator = query.fetch(limit=limit, start_cursor=cursor)
    page = next(query_iterator.pages)

    entities = builtin_list(map(from_datastore, page))
    next_cursor = (
        query_iterator.next_page_token.decode('utf-8')
        if query_iterator.next_page_token else None)

    return entities, next_cursor

このコードは、datastore.Query を使用してデータストアへのクエリを実行し、すべての Book エンティティを取得して title 順に返します。クエリにフィルタを適用することもできます。詳細については、Datastore ドキュメントをご覧ください。

クエリで要求するイテレータを取得するために query.fetch を使用します。一度に 1 ページずつ結果を取得し、ユーザーが結果の次のページを読み込むことができるようにカーソルを返します。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...