SEワンタンの独学備忘録

IT関連の独学した内容や資格試験に対する取り組みの備忘録

【MongoDB】FlaskのWebアプリからMongoDBに接続する

いろいろやってきた環境構築を繋げます。

前提とか

アプリ側(Flask)の環境構築は以下とか。

www.wantanblog.com

DB側(MongoDB)の環境構築は以下とか。

www.wantanblog.com

上記と同等レベルの環境構築が完了していることを前提としています。
今回レベルの実装なら多分プログラムの実装より環境構築の方が大変かもしれないです。

全て同一サーバ(VirtualBox)上に構築しています。

全体像

構築イメージ

f:id:wantanBlog:20200407004622p:plain

なんとなくのイメージ図です。
ブラウザなどからアクセスがあるとFlaskアプリである「main.py」にアクセスがあり(①)、そこから今回のメイン部分であるMongoDBへのアクセス(②、③)があります。

取得したデータをそのまま返すだけでは味気なかったので、HTMLファイルにDBから取得したデータを埋め込んで(④、⑤)、HTMLをブラウザに返却します。

ディレクトリ構成

/usr/share/nginx/app/sample
 ├main.py
 └templates
  └index.html

ルートはどこでもよいです。
今回の場合はNginxの環境構築のものを引き継いでいるので「/usr/share/nginx/app/sample」をルートしてそこに「main.py」を配置します。
pythonソースの名前は任意ですが、HTMLファイルを格納するディレクトリ名は原則固定なようです
pythonソースと同じ階層に「/templates/」というディレクトリを作成しそこにHTMLファイルを格納します。

ソース

このあと細かく見ていきますが先にサンプルソースを掲載しておきます。
Flask(Python)のソースとhtmlファイルです。

・main.py

from flask import Flask, render_template
from pymongo import MongoClient

application = Flask(__name__)

#デフォルトルート
@application.route('/')
def index():
    client = mongo_init()
    sample = client.test.doc.find()
    return render_template('index.html',contents=sample)

def mongo_init():
    host = 'localhost'
    port = 27017
    db = 'test'
    user = 'wantan'
    pwd = 'wantan'
    client = MongoClient(host, port)
    client[db].authenticate(user, pwd)
    return client

・index.html

{% block page_content %}
<h1>Sample List</h1>
<div>
    <table>
      <thread>
        <tr>
          <th>name</th>
          <th>num</th>
          <th>type</th>
        </tr>
      </thread>
      <tbody>
        {% for sample in contents %}
        <tr>
          <td>{{ sample.name }}</td>
          <td>{{ sample.num }}</td>
          <td>{{ sample.type }}</td>
        </tr>
        {% else %}
          <td>no entry exist!</td>
        {% endfor %}
      </tbody>
    </table>
  </div>
</div>

{% endblock %}

MongoDBに接続する

アクセス準備
DBへのアクセス

・main.pyの抜粋

# 1)MongoClientをインポートする
from pymongo import MongoClient

# Mongoクライアントオブジェクトの初期化関数
def mongo_init():
    #2)変数の設定
    host = 'localhost'
    port = 27017
    db = 'test'
    user = 'wantan'
    pwd = 'wantan'
    # 3)クライアントオブジェクトの生成
    client = MongoClient(host, port)
    # 4)DBのユーザ認証
    client[db].authenticate(user, pwd)
    # クライアントを返却する
    return client

1)MongoClientをインポートする
pythonでMongoDBを使用するには「MongoClient」と言われるクラスをpymongoからインポートします。
インストールされていない場合には先にサーバでpipからインストールするようにしましょう。

2) 変数の設定
クライアントオブジェクトを生成するための変数を設定しています。もちろん直接指定しても問題はありません。

3)クライアントオブジェクトの生成
インポートしたMongoClientで接続先を指定してオブジェクトを生成します。

4)DBのユーザ認証
ユーザ認証を設定している場合には、ユーザ認証を行います。
認証を設定しない場合にはそのままでも使用できるはずです。
注意点としては「client[db]」でDBを指定する必要がある点ぐらいでしょうか。


最後に生成したクライアントオブジェクトを呼び出し元に返却します。

DBからドキュメントを取得

クライアントの生成が正しくできていればあとは取得するだけです。

#デフォルトルート
@application.route('/')
def index():
    # 1)クライアントオブジェクトを初期化する
    client = mongo_init()
    # 2)ドキュメントの全取得
    sample = client.test.doc.find()
    # 3)テンプレートに取得値を埋め込み返却
    return render_template('index.html',contents=sample)

1)クライアントオブジェクトを初期化する
前述のとおり、オブジェクトを生成し接続先指定、認証を行う。

2)ドキュメントの全取得
対象のコレクションからドキュメントを全取得します。
client.[DB].[コレクション].[命令]の形式で指定をします。
テーブルの結合は原則できませんが、挿入更新削除、条件を絞った検索など一般的なRDBと同様の命令が実行できます。今回はもっとも簡単で分かりやすい全取得のみ。

3)テンプレートに取得値を埋め込み返却
後述します。

HTMLファイルを返却する

HTMLファイルを読み込む

flaskを使用すると簡単にHTMLファイルも読み込めるようなのでおまけでつけました。

・main.pyの抜粋

# 1) render_templateをインポート
from flask import Flask, render_template

def index():
     # 2) HTMLファイルの読み込み
     return render_template('index.html',contents=sample)

1) render_templateをインポート
HTMLファイルの読み込みのため、flaskからrender_templateをインポートします。
flaskがインストールできていればそのままインストールできるはずです。

2) HTMLファイルの読み込み
render_templateの第一引数が読み込みを行うHTMLファイル(テンプレート)になり、前述のディレクトリ構成を作りファイルを配置すればそれだけで読み込めるようになるみたいです。
第二引数には埋め込みを行うパラメータを指定します。
MongoDBの場合は取得値をそのまま渡せば、それだけで正しくパラメータリストとして認識されるようです。

HTMLファイル側の実装については後述します。

HTMLファイルにパラメータを埋め込む

・index.htmlの抜粋

        <!-- 1)パラメータリストをfor文で列挙する -->
        {% for sample in contents %}
        <tr>
          <!-- 2)オブジェクトから要素を取り出して出力する -->
          <td>{{ sample.name }}</td>
          <td>{{ sample.num }}</td>
          <td>{{ sample.type }}</td>
        </tr>
        <!-- 一件も存在しない場合のデフォルト出力-->
        {% else %}
          <td>no entry exist!</td>
        <!-- for文の終わり-->
        {% endfor %}

1)パラメータリストをfor文で列挙する
contentsというパラメータリストをfor文によりsampleと名付けたオブジェクトごとに出力していきます。

2)オブジェクトから要素を取り出して出力する
sampleオブジェクトから要素を取り出していき出力します。
オブジェクト名はその場で勝手に命名しましたが、要素名はMongoDB内のドキュメントの列に対応しています。

画面の表示結果

最後にブラウザからアクセスした際の画面表示結果は以下のようになります。
リストで表示するようにしたとはいえ、CSSがないと何とも味気ない感じになりますね。

f:id:wantanBlog:20200408000028p:plain


ーーーーーーーーーーーーーー
とりあえずここまででひと段落になります。次回以降は本流に戻る予定。