LoginSignup
4

More than 3 years have passed since last update.

posted at

updated at

Python/Flaskで配列をDB代わりにし、form入力・表示・削除するアプリを作る。

はじめに

こんにちは。
アドベントカレンダー2019のPythonカテゴリのエントリです。

2018年年末からPythonを趣味で書き始めた、異業種、実務未経験の者です。
今日は、Flaskを使って、モデル、ビューを書いてみて、色々苦労したのでそれを書きたいと思います。
特に、削除はめちゃめちゃ苦労しました。
色々おかしなところがあるかも知れませんが、一応機能しました。
初心者向けとなります。

完成アプリ

スクリーンショット 2019-12-08 16.15.32.png

完成コード

app.py
from flask import Flask,render_template,request,redirect
app = Flask(__name__)

class DatabaseInit():
    db = []
    def __init__(self):
        pass

class DatabaseManager(DatabaseInit):
    def db_append(self,add_value):
        self.db.append(add_value)
    def db_get(self):
        return self.db
    def db_delete(self,value):
        self.db.pop(value)

@app.route('/', methods=['POST','GET'])
def index():
    if request.method == 'GET':
        db = DatabaseInit()
        return render_template('index.html')
    if request.method == 'POST':
        db_manage = DatabaseManager()
        num = request.form.get('comment')
        if num == None:
            view_lists = db_manage.db_get()
        else:
            db_manage.db_append(num)
            view_lists = db_manage.db_get()
        return render_template('index.html', show_lists = view_lists)

@app.route('/delete', methods=['POST'])
def delete():
    db_manage = DatabaseManager()
    id = int(request.form['delete_id'])
    db_manage.db_delete(id)
    return redirect('/',code=307)

if __name__ == '__main__':
    app.run(host='127.0.0.1',port='5000',debug=True)
index.html
<html>
    <head>
        <title>Flask</title>
    </head>
    <body>
        <h1>FlaskWEBアプリ</h1>
        <h2>コメント登録</h2>
        <form action="/" name="comment" method="POST">
            <input type="text" name="comment">
            <input type="submit" name="submit_comment">
        </form>
        <h2>コメント表示</h2>
        {% for show_list in show_lists %}
        {{ show_list }}
        <form method="POST" name="delete" action="/delete">
            <input type="hidden" name="delete_id" value="{{ show_lists.index(show_list) }}">
            <input type="submit" name="delete_id" value="削除">
        </form>
    </br>
        {% endfor %}
    </body>
</html>

書く前に考えたこと

  • SQLがフルスクラッチでは書けなかったので、配列をデータベース代わりに
  • データベースをモデルクラスで操作
  • ファイル数を極力少なくする

環境

  • Mac
  • Python3.7.2

実装

仮想環境

python -m venv venv

venvを起動

source venv/bin/activate

Flaskをインストール

pip install flask

ファイルを作成

touch app.py

テンプレートを作成

Flaskはtemplatesフォルダ内にhtmlを入れる必要があります。

mkdir templates
cd templates
touch index.html

構成

/作業フォルダ
┝app.py
┗/templates
 ┗index.html

HelloWorld

app.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello World'

if __name__ == '__main__':
    app.run()
python app.py

スクリーンショット 2019-12-08 14.15.37.png

デバッグモード、ホスト、ポートを設定

デバッグモードをTrueにしておくと、ブラウザを更新するだけで反映されるので楽です。
@app.route('/')としておくと、GET扱いになります。
つまり、ルートURLにアクセスした時に表示される画面になります。

app.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello World'

if __name__ == '__main__':
    app.run(host='127.0.0.1',port='5000',debug=True)

htmlをレンダリング

render_templateをインポートします。

index.html
<html>
    <head>
        <title>Flask</title>
    </head>
    <body>
        <h1>Hello World</h1>
        <p>これはhtmlです。</p>
    </body>
</html>
app.py
from flask import Flask,render_template
app = Flask(__name__)


@app.route('/')
def index():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(host='127.0.0.1', port='5000', debug=True)

スクリーンショット 2019-12-08 14.26.43.png

htmlで画面を作成

action='/' は、'/'にデータを送る、ということになります。例えば、他のページに遷移する場合は、'/something'などと書けば、そのURLに対してデータを送信します。
今回はシングルページなので、今いる'/'にデータを送信します。
method="POST"は、データをPOSTで送信します。
GETはURLに公開してデータを送信、POSTは隠してデータを送信する、というようなイメージです。
今回は、GETの時に初期化されたページを表示、POSTの場合にフォームで送られたデータを更新したページを表示、します。
name='comment'は、app.pyでデータを受け取るために必要です。好きな文字列で大丈夫です。

index.html
<html>
    <head>
        <title>Flask</title>
    </head>
    <body>
        <h1>FlaskWEBアプリ</h1>
        <h2>コメント登録</h2>
        <form action="/" name="comment" method="POST">
            <input type="text" name="comment">
            <input type="submit" name="submit_comment">
        </form>
        <h2>コメント表示</h2>
    </body>
</html>

スクリーンショット 2019-12-08 14.37.23.png

htmlにデータを渡して表示してみる

Flaskはjinja2というテンプレートエンジンを使用しています。
python側では、render_template()関数の引数に、html側の変数名=python側の変数名、を書きます。
html側では、{{ html側の変数名 }}とすると表示できます。

app.py
from flask import Flask,render_template
app = Flask(__name__)


@app.route('/')
def index():
    show_py = 'Pythonからきました'
    return render_template('index.html',show_html = show_py)

if __name__ == '__main__':
    app.run(host='127.0.0.1', port='5000', debug=True)
index.html
<html>

<head>
    <title>Flask</title>
</head>

<body>
    <h1>FlaskWEBアプリ</h1>
    <h2>コメント登録</h2>
    <form action="/" name="comment" method="POST">
        <input type="text" name="comment">
        <input type="submit" name="submit_comment">
    </form>
    <h2>コメント表示</h2>
    {{ show_html }}
</body>

</html>

スクリーンショット 2019-12-08 14.57.20.png

データベース(今回は配列)とモデルを作成

今回はデータベースを配列でやります。
DatabaseInitで配列を初期化します。
DatabaseManagerでメソッドを作り、そのメソッドを使ってデータベースを操作します。
今回は、要素を追加、要素を取得、要素を削除の3つの機能を作りました。
今回は配列を利用しましたが、SQLやDBに書き換えさえすれば、考え方は同じかと思います。

passは何も書きたくないけれど何か書く必要がある場合、書きます。要するに何もしないということです。
selfはインスタンスが代入されます。
Pythonの場合、各メソッドの第一引数は必ずselfです。
def __ init __(self):はインスタンス化された時に実行されます。
class DatabaseManager(DatabaseInit)は、継承です。リストdbにアクセスするため継承しています。

app.py
class DatabaseInit():
    db = []
    def __init__(self):
        pass

class DatabaseManager(DatabaseInit):
    def db_append(self,add_value):
        self.db.append(add_value)
    def db_get(self):
        return self.db
    def db_delete(self,value):
        self.db.pop(value)

ルーティングと処理を作成

method=['GET','POST']として、GET、POST両方でアクセスできるようにします。
GETは一番最初にアクセスした時に見る画面、POSTはデータが送信された時に見る画面です。
それを、if文で処理します。
request.method==で判定します。
db = DatabaseInit()でDatabaseInitクラスをインスタンス化します。
つまり、今回はdbという空の配列を作成しています。
POSTの方の処理は、更に入れ子のif文があります。
htmlのformで文字列を送信する処理と、削除する処理を分けるためです。
num = request.form.get('comment')は、htmlのformで文字列を送信した際に代入されます。
if num == None:とありますが、つまり削除が送信された時にこちらの処理に誘導します。
文字列が送信された時に、else:以下が処理されます。
db_manage.db_append(num)で、num変数に代入されている文字列を配列に加えています。
db_manage.db_get()で配列を取得しています。

app.py
from flask import Flask,render_template,request,redirect
app = Flask(__name__)

@app.route('/', methods=['POST','GET'])
def index():
    if request.method == 'GET':
        db = DatabaseInit()
        return render_template('index.html')
    if request.method == 'POST':
        db_manage = DatabaseManager()
        num = request.form.get('comment')
        if num == None:
            view_lists = db_manage.db_get()
        else:
            db_manage.db_append(num)
            view_lists = db_manage.db_get()
        return render_template('index.html', show_lists = view_lists)

htmlでshow_listsを表示

jinja2では、{% %}で囲むことで、pythonコードを埋め込むことができます。
リストを順々に出力したいので、{% for xx in xxx %}~{% endfor %}で繰り返したい処理を囲います。
{% if 式 %}~{% endif %}だとifが書けます。

index.html
<html>
    <head>
        <title>Flask</title>
    </head>
    <body>
        <h1>FlaskWEBアプリ</h1>
        <h2>コメント登録</h2>
        <form action="/" name="comment" method="POST">
            <input type="text" name="comment">
            <input type="submit" name="submit_comment">
        </form>
        <h2>コメント表示</h2>
        {% for show_list in show_lists %}
        {{ show_list }}
        <form method="POST" name="delete" action="/delete">
            <input type="hidden" name="delete_id" value="{{ show_lists.index(show_list) }}">
            <input type="submit" name="delete_id" value="削除">
        </form>
    </br>
        {% endfor %}
    </body>
</html>

一番ハマったのが、デリート部分でした。
通常のformだと、POST、GETしか送信できない仕様なので、type="hidden"を使って、postで、name="delete_id"とvalue="{{ show_lists.index(show_list) }}"を送ります。
画面には映りません。
ちょっとformの認識が甘いので、nameの記述の必要がないところがあるかも知れません。。誰か教えてください。。

/deleteをルーティング

削除ボタンが押されたら、postで、削除する配列のインデックス値を取得し、db_delete()メソッドで配列から要素を削除します。
request.formで取得したデータは文字列なので、int()で型変換します。
redirect()は強制的にページを移動させるということです。render_template()だと、今いるURLにページを作成してしまうので、local/delete/index.htmlとか変なURLになってエラーになってしまいます。
'/'とすることで、def index():関数にルーティングされます。
code=307は、POSTでリダイレクトする、という意味です。
リダイレクトはGETでデータ送信されてしまうので、codeを307にセットすることで、POSTを維持する、という操作になります。

app.py
@app.route('/delete', methods=['POST'])
def delete():
    db_manage = DatabaseManager()
    id = int(request.form['delete_id'])
    db_manage.db_delete(id)
    return redirect('/',code=307)

以上です

初めてきちんとQiitaに技術っぽいものを書きましたので、なかなか解説が足りていない部分も多いかとは思いますが、どなたかのお役に立てれば幸いです。
間違いなどがありましたら、ご指摘いただければ修正をさせて頂きます。

今度はDBを使ってやってみたいと思います!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
4