flaskチートシート

flaskもよく忘れるのでチートシートを作ってみました

# 一番基本
``` python:app.py
from flask import Flask				#Flaskをインポート

app = Flask(__name__)				#インスタンス作成

#ルーティングはデコレータがやってくれる
@app.route('/aaa', methods=['GET'])	#/aaaにgetメソッドでアクセスされたとき以下実行
def aaa():							#中身の関数
    return "<h1>hello</h1>"			#helloを太字で出力
	
if __name__ == "__main__":			#いつものやつｗ
    app.run(debug=True)				#デバッグオンで動かすと、コード変更時に楽なので
```

### 起動方法
サーバー起動　	python app.py
ブラウザからは　http://127.0.0.1:5000/aaa　でアクセス


# テンプレートを使う（jinja2）
	htmlファイルは必ずtemplatesフォルダに格納すること


```python:app.py
from flask import Flask, render_template	#新たなメソッドの導入

app = Flask(__name__)

@app.route('/aaa', methods=['GET'])
def aaa():
    return render_template("bbb.html")		#render_templateでbbb.htmlを呼び出す

if __name__ == "__main__":
    app.run(debug=True)
```
```html:bbb.html
<html><head><meta charset="utf-8"></title></style></head> 
<body>
  <h3>world</h3>
</body></html>
```

# テンプレートにパラメータを渡す
```python:app.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/aaa', methods=['GET'])
def aaa():
    return render_template("ccc.html",x=" wide",y=" web ")#ここで引数を渡している

if __name__ == "__main__":
    app.run(debug=True)
```

うけとった引数を表示するには {{引数}} のように書く
```html:ccc.html
<html><head><meta charset="utf-8"></title></style></head> 
<body>
  <h3>world{{x}}{{y}}</h3>
</body></html>
```


# htmlからformでデータを送信（GET）
```python:app.py
from flask import Flask, request, render_template
app = Flask(__name__)

@app.route('/', methods=['GET'])#フォームの送信
def index():
    return render_template('form.html')

@app.route('/ggg', methods=['GET'])#結果の処理
def index2():
    name = request.args.get('name')
    return render_template('form2.html', name=name)

if __name__ == '__main__':
    app.run(debug=True)

```
```html:form.html
<!DOCTYPE html>
<html>
<head>
    <title>Flask Form GET</title>
</head>
<body>
    <form action="/ggg" method="get">
        <input type="text" name="name">
        <input type="submit" value="Submit">
    </form>
</body>
</html>
```

結果を表示するためのhtml
```html:form2.html
</head>
<body>
  <h2>{{name}}</h2>
</body>
</html>
```

# htmlからformでデータを送信（POST）

```python:app.py
from flask import Flask, request, render_template
app = Flask(__name__)

@app.route('/', methods=['GET'])#フォームの送信
def index():
    return render_template('formP.html')

#結果の処理部分
@app.route('/ggg', methods=['POST'])　#POSTになっている点に注目
def index2():
    name = request.form.get('name')
    return render_template('form2.html', name=name)

if __name__ == '__main__':
    app.run(debug=True)

```

フォーム（method="post"になっている）
```html:formP.html
<!DOCTYPE html>
<html>
<head>
    <title>Flask Form GET</title>
</head>
<body>
    <form action="/ggg" method="post">
        <input type="text" name="name">
        <input type="submit" value="Submit">
    </form>
</body>
</html>
```

結果表示のためのhtml
```html:form2.html
</head>
<body>
  <h2>{{name}}</h2>
</body>
</html>
```


### テンプレート（jinja）に辞書を渡してキーとバリューを表示
前提　dicに辞書が入っている、pythonからは　render_template(aaa.html result=dic)とする
```html:aaa.html
    <ul>
        {% for key, value in results.items() %}
            <li>{{ key }}: {{ value }}</li>
        {% endfor %}
    </ul>
```


# リダイレクト

```python:app.pyの一部
from flask import Flask, redirect, url_for
```


リダイレクトの実装
以下の例は、/old にアクセスすると /new にリダイレクトされます
```python:app.pyの一部
@app.route('/old')
def old_endpoint():
    return redirect(url_for('new_endpoint'))

@app.route('/new')
def new_endpoint():
    return "This is the new endpoint!"
```
リダイレクトは意外とわかりにくいので細かく解説します。

これのフローイメージは以下のとおりです

![リダイレクト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411052/6464f8d1-dd81-4e13-befa-310b067c1b60.png)



①リクエスト(/old): クライアントが /old エンドポイントにアクセスを試みます。
②リダイレクト命令: サーバーはクライアントに対して、新しいエンドポイント /new にリダイレクトするよう命じます。これは通常、HTTPステータスコード302（一時的リダイレクト）または301（永続的リダイレクト）で行われます。
③リクエスト(/new): クライアントはサーバーからのリダイレクト指示に従い、自動的に /new エンドポイントに新たなリクエストを送信します。
④レスポンス文字列: /new エンドポイントからの応答として「This is the new endpoint!」という文字列がクライアントに送られます。

サーバー側のコードを再掲します。図と対比させてみてください。
```python:app.pyの一部
@app.route('/old')　　　　　　　　　　　　　　　#①でココに飛んでくる
def old_endpoint():
    return redirect(url_for('new_endpoint'))　#②のリダイレクト命令

@app.route('/new')　　　　　　　　　　　　　　　#③でここに飛んでくる
def new_endpoint():
    return "This is the new endpoint!"　　　　#④のレスポンス文字列送信
```


#### そもそもリダイレクトとは？

たとえばサイトを引っ越したり、ページ名を変更したり（/about→/company）、ログインしていないのでログインページに誘導したり、モバイルユーザーをモバイル用のページへ誘導といった目的で使います、もしリダイレクトがなければ、古いURLにアクセスしたユーザーがエラー画面や空白ページを見てしまうことになります。
このように ユーザーを正しい場所に誘導するために存在するのがリダイレクトです。

その方法としてHTTPステータスコードの 301や302が使われます。

#### ステータスコード
ステータスコードは3桁の数字で、サーバーからの返事（成功、失敗、転送など）です。
最初の1桁でおおまかな「カテゴリ」が決まります。

1xx   リクエスト継続中（ほぼ使わない）
2xx   処理に成功
3xx	  リダイレクト
4xx	  エラー
5xx   サーバーエラー

200番代とか400番代は見たことがあるのではないでしょうか
300番代がリダイレクトに使われるコードです。




# パスワードの暗号化

Flaskでパスワードを暗号化するために、bcryptというライブラリを使用します

```
pip install Flask-Bcrypt
```


```python:app.pyの一部
from flask import Flask
from flask_bcrypt import Bcrypt

app = Flask(__name__)
bcrypt = Bcrypt(app)
```


### パスワードの暗号化
ユーザーから受け取ったパスワード（pw）を暗号化します。
```python:app.pyの一部
hashed_pw = bcrypt.generate_password_hash(pw).decode('utf-8')
```

### パスワードのチェック
ユーザーがログイン時に入力したパスワードと、データベースに保存されているハッシュ化されたパスワードを比較
```python:app.pyの一部
check = bcrypt.check_password_hash(hashed_pw, pw)
if check:
    print("Password is correct!")
else:
    print("Password is incorrect!")
```

# セッション　クライアント側にデータを保持する場合
セッションデータをクライアント側に持つのは、あまり良くないとされていますが、デフォルトの場合はこんな使い方になります

```python:app.pyの一部
from flask import Flask, session

app = Flask(__name__)
app.secret_key = 'secret_key'# セッションの暗号化に使用される秘密鍵

# セッションから情報を取得する
var = session.get('result')  
#または
var = session['result']
```

キーがセッションに存在するかどうかが不明確な場合、KeyErrorを防ぐために session.get('result') のようなメソッドを使用することが推奨されます。しかし、キーの存在が保証されている場合、直接 session['result'] としてアクセスすることができます。



# セッション　サーバー側にデータを保持する場合

flaskはセッションデータをデフォルトではクライアント側に保持するのですが、安全性を高めたいときはサーバー側に持つのが良いとされています。（phpやdjangoではデフォルトがサーバー側）。というわけでサーバー側に保持する場合のやり方です。

```python:app.pyの一部
from flask import Flask, session
from flask_session import Session

app.config['SECRET_KEY'] = 'your_secret_key_here'#秘密鍵
app.config['SESSION_TYPE'] = 'filesystem'#ファイルに保存
Session(app)
```


このあとは普通に
```python:app.pyの一部
var = session.get('result')   #キーがない可能性がある場合
session["result"] = var       #キーが存在する場合
```
↑書き込みの場合

```python:app.pyの一部
var = session["result"]
```
↑読み込みの場合（キーの存在を気にせず直接書き込めます）


# flash()関数

シンプルなようで意外とわかりにくいのがflash()関数。chatGPTに聞くと
以下のように返ってきます
ーーーーーーーーーー
Flaskのflash()関数は、ユーザーへの一時的なメッセージを提供するために使用されます。これは、例えばフォームのバリデーションエラーや操作の成功・失敗の通知など、一時的なフィードバックが必要な場面で役立ちます。flash()関数で生成されたメッセージは、リクエスト間で一時的に保存され、次のリクエストで表示された後に消えます。
ーーーーーーーーーー

なるほどわからん！　
私はセッション付きのrender_template()関数と理解しました。以下サンプルとリクエストレスポンスのフローイメージです

```python:app.py
from flask import Flask, flash, redirect, url_for, render_template
app = Flask(__name__)
app.secret_key = '!!#$%'  # セッションデータを暗号化するためのキー

@app.route('/sample')
def sample():
    flash('This is a flash message!')
    #このメッセージは最終的にindex.htmlに渡されるが一時的にセッションに保存
    return redirect(url_for('index'))

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

if __name__ == '__main__':
    app.run(debug=True)
```

```　index.html
<!DOCTYPE html>
<html>
<head>
    <title>Flash Example</title>
</head>
<body>
    {% for message in get_flashed_messages() %}#ここでセッションから取り出される
        <div>{{ message }}</div>
    {% endfor %}
</body>
</html>
```

### 上のコードでのやり取り　（リクエストレスポンスのフローイメージ）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411052/ea6d9f9d-e965-5311-0254-5332fc764c5f.png)





# blueprint
webアプリの機能が増えてくると、とうぜんファイルを分割したくなります。このときappというインスタンス(一番さいしょｎ基本でつくったやつ)を各モジュールの中で使い回すのがやっかいになってきます。

そこで、appインスタンスに相当するインスタンスを各モジュールで作成しておいてapp.pyではこれらをimportし、登録しておくと機能を追加してもモジュール単位で独立しているので管理がしやすくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411052/43af635a-374b-4dc6-a9c6-c1eb0b6b4dc9.png)



ちなみにappインスタンスとは
```python:app.pyの一部
from flask import Flask				#Flaskをインポート
app = Flask(__name__)				#インスタンス作成
```
でつくったインスタンスで、run()やroute()などのメソッドやプロパティをもっています
chatGPTに調べてもらったら以下のようにでてきました

メソッド
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411052/a8f034c8-2fb6-4245-b11d-416eec50513f.png)

プロパティ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411052/3abf6849-9007-4246-bba8-97f2b06b046a.png)