LoginSignup
2
4

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-12-08

はじめに

こんにちは。
アドベントカレンダー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を使ってやってみたいと思います!

2
4
0

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
  3. You can use dark theme
What you can do with signing up
2
4