今回の記事では、「Docker」で「flask」+「PostgreSQL」環境構築からPostgれSQLからデータを取得し、そのデータをページに表示するまでをご紹介する内容です。Dockerfileを準備しているので、細かな環境構築はDockerで省略できます。初学者でもわかるように記載していくので是非参考にしていただければ幸いです。
「Docker」に「Flask」+「PostgreSQL」環境構築
まずは下記のプロジェクト構成を準備してください。
.
└── flask
├── app
└── dev
├── Dockerfile
├── docker-compose.yml
└── init_db
└── init.sql
ではコンテナ立ち上げ用のDockerfileにPythonのイメージとflaskとPostgresSQLの接続クライアントのインストールコマンドを記述します。PostgreSQLはdocker-composeのみで記述していく方針ですのでDockerfileに記述はなしです。
From python:3
RUN pip install --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org flask
# psycopg2(PostgreSQLクライアント)のインストール
RUN pip install psycopg2
これで準備万端です。Dockerを起動して環境構築してください。
Flaskデータ保存ページ表示
コンテナに入っての作業になります。まずは下記でコンテナIDを接続し、そのコンテナ入りまょう。
docker ps
[起動中のコンテナが表示]現在二つのコンテナが起動しています。
FlaskとPostgreSQLです。Flaskの方に入ります。
docker exec -it [コンテナID] /bin/bash
次にhtmlなどの表示を行うために表示を整えます。今回の記事は下記サイトを参考にしております。下記サイトのDocker-compose.ymlファイルはマウント位置が微妙に間違っているようなのでこちらのサイトの上記ファイルを参考にする以外は基本的に内容は同じになっております。
下記コマンドで構成はできます。
cd usr/local/src/work
mkdir static/css static/js templates
touch app.py static/css/style.css static/js/script.js templates/_layout.html templates/hello.html
コンテナ起動時にマウントしているのでファイル作成はローカルにも反映されます。逆にローカルでフォルダを作成してもコンテナ上に反映されます。
下記ファイルを作成してください。
from flask import Flask, render_template, request, jsonify
import psycopg2
app = Flask(__name__)
def getDbConnection():
return psycopg2.connect("postgresql://test:test@db:5432/test")
#"postgresql://[設定したID]:[設定したPS]@[設定したID]:5432/[設定したID]"
def getInputs(con):
inputs = []
with con.cursor() as cur:
cur.execute("SELECT * FROM INPUTS")
for row in cur:
inputs.append("[" + str(row[1]) + "]" + row[0])
return inputs
def insertInput(con, value):
with con.cursor() as cur:
cur.execute("INSERT INTO INPUTS (value, input_date) VALUES (%s,current_date)", (value, ))
@app.route("/")
def index():
return "Hello world!"
@app.route("/hello")
def hello():
with getDbConnection() as con:
return render_template("hello.html", title = "Helloページ", inputs = getInputs(con))
@app.route("/hello", methods=["POST"])
def hello_post():
name = request.form["name"]
message = request.form["message"]
inputValue = name + ":" + message
with getDbConnection() as con:
insertInput(con, inputValue)
return render_template("hello.html", title = "Hello(post)ページ", input1 = "入力値=[" + inputValue + "]", inputs = getInputs(con))
@app.route("/api/hello/post", methods=["POST"])
def hello_post_ajax():
name = request.json["name"]
message = request.json["message"]
inputValue = name + ":" + message
with getDbConnection() as con:
insertInput(con, inputValue)
return jsonify({"input1": "入力値=[" + inputValue + "]", "input": inputValue})
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.1.min.js"></script>
<script src="/static/js/script.js"></script>
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous">
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="http://example.com" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
<div class="dropdown-menu" aria-labelledby="dropdown01">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</nav>
<main class="main">
{% block content %}
{% endblock %}
</main>
</body>
</html>
{% extends '_layout.html' %}
{% block content %}
<div class="jumbotron">
<div class="container">
<h1 class="display-3">Hello, world!</h1>
<p>This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more »</a></p>
</div>
</div>
<div class="container">
<form method="POST" action="/hello">
<div class="form-group">
<label for="name">名前</label>
<input type="text" class="form-control" id="name" name="name">
</div>
<div class="form-group">
<label for="message">メッセージ</label>
<input type="text" class="form-control" id="message" name="message">
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">送信</button>
<button class="btn btn-lg btn-danger btn-block" type="submit" id="btnSendAjax">非同期送信</button>
</form>
<span id="lblInput1" class="hogelabel">{{input1}}</span>
<ul id="listInputs">
{% for value in inputs %}
<li>{{value}}</p>
{% endfor %}
</ul>
</div>
{% endblock %}
body {
background-color: antiquewhite;
}
$(function() {
$('#btnSendAjax').click(function () {
// 入力値取得
var name = $('input[name=name]').val();
var message = $('input[name=message]').val();
$.ajax({
url: "/api/hello/post",
type: "POST",
data: JSON.stringify({name: name, message: message}),
dataType: "json",
contentType: "application/json",
success: function (XMLHttpRequest, textStatus, errorThrown) {
if (textStatus == 'success' || textStatus == 'nocontent') {
// 成功時
$('#lblInput1').text(XMLHttpRequest.input1);
$('#listInputs').append($("<li>").text(input));
} else {
// エラー時
console.error(XMLHttpRequest.responseJSON.Message);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
// エラー時
console.error(XMLHttpRequest.responseJSON.Message);
},
});
return false;
});
});
これでファイル構成は完了です。
下記でflaskを起動してください。
flask run -h 0.0.0.0
ホストのブラウザから localhost:5000 にアクセスして「Hello world!」と表示されたら成功です。
また、「http://localhost:5000/hello」でサンプルページが表示され、「送信」・「非同期送信」ボタンで画面入力した文字がDBに登録されます。確認してみてください。
では今回の記事は以上です。他にも多数のPython関連お記事を記載しているので興味がある方は是非際都内見ていってください。
コメント
[…] 【Flask】「Docker」上に「 Flask + PostgreSQL 」環境を構築し、データベースに接… […]