備忘録です。
FlaskでのWebサービスの作り方をミニマムで。
必要なライブラリ
from flask import Flask
from flask import render_template, request, redirect
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from datetime import datetime
import pytz
テンプレート関係とデータベースを扱うためのSQLAlchemy、Migrate、そしてそのデータベースに保存した日時を扱うためのdatetime、日付処理を扱うためのpytzです。
render_templateとredirectの違い
redirect
引数は遷移先のurl、その他の変数等は渡しません。
@app.route()で指定したページにアクセスすると、単純にそのURLに遷移します。
render_template
引数でurl以外を渡すことができる
@app.route()へGETでアクセスした場合は、引数で渡されたurlのViewを返します。
※こういうことらしい
redirectは、Locationヘッダーをindex関数のURLとして、302ヘッダーをブラウザーに返します。 render_templateは200を返し、index.htmlテンプレートはそのURLのコンテンツとして返されます。
参考:
python — render_templateとリダイレクトの違いは?
Flaskアプリの定義
app = Flask(__name__)
まずはこれから。Flaskのインスタンスを作成し、変数に格納する。
__name__ というのは、自動的に定義される変数で、現在のファイルのモジュール名が入ります。
データベースの設定方法
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
migrate = Migrate(app, db)
class post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text(), nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.now(pytz.timezone('Asia/Tokyo')))
SQALchemyはORM ( Object Relational Mapper ) で、PythonのClassとして定義した内容を、SQL文に変換(mapping)します。つまり、SQL文を直接記述せずに、通常のオブジェクトを扱うようにデータベースを扱うことができるようになります。
1行目:アクセスするDBの種類と位置を指定している
2行目:警告が出るのでこちらを記述
3行目:インスタンスの設定。dbという変数に格納
5行目:クラスの作成。クラスの中で各カラムの設定をします。
※Text()に引数(文字数)が入っていないのはheroku用です。
共通のテンプレート部分
<!DOCTYPE html>
<html lang="ja" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport", content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{{ url_for('static', filename='css/sample.css')}}">
<title>ブログアプリケーション</title>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
他のテンプレートでextendsで継承すると、このテンプレートに記述したものが共通部分となります。{% block content %}{% endblock %}間に入ります。
また、bootstrapのCDNのリンクも<head></head>間で記述します。
{% %}はテンプレートタグといいこの中にpythonのコードを直接記述可能。必ず{% end○○ %}という形で閉じること
トップページ
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
posts = Post.query.all()
return render_template('index.html', posts=posts)
上記はapp.pyで定義。index.htmlを動かす。
Post.query.all()でデータベースPost内の全てをpostsという変数に入れindex.htmlを呼び出すときに渡している。
{% extends "base.html" %}
{% block content %}
<div class="container">
<h1>ブログアプリケーション</h1>
<a href="/create" role="button">新規作成画面</a>
<<a href="/logout" role="button">ログアウト</a>>
{% for post in posts %}
<article>
<h2>{{post.title}}</h2>
<a href="/{{post.id}}/update" role="button">編集</a>
<a href="/{{post.id}}/delete" role="button">削除</a>
<p>作成日時:{{post.created_at}}</p>
<p>{{post.body}}</p>
</article>
{% endfor %}
</div>
{% endblock %}
そして、app.pyを受けたindex.htmlを作成。
最初に{% extends "base.html" %}という形で共通テンプレートの継承を行う。
app.pyなどで定義した引数は{{ }}内に記述し呼び出せる。
※render_templateでテンプレートを割り当てるときに引数として渡すこと。
投稿ページ
@app.route('/create', methods=['GET', 'POST'])
def create():
if request.method == 'POST':
title = request.form.get('title')
body = request.form.get('body')
post = Post(title=title, body=body)
db.session.add(post)
db.session.commit()
return redirect('/')
else:
return render_template('create.html')
データベースへアクセスするにはPOSTでアクセス。
create.html内のform内のnameで入力データを取得し、それらをまとめて、postという変数に入れ、db.session.add(post)で、それぞれのカラムへ追加する。
{% extends "base.html" %}
{% block content %}
<h1>新規登録</h1>
<form method="POST">
<label for="title">タイトル</label>
<input type="text" name="title">
<label for="body">内容</label>
<input type="text" name="body">
<input type="submit" value="新規登録">
</form>
{% endblock %}
こちらは投稿ページの例
formタグで記述しPOSTリクエストを送っている。
記事を更新
@app.route('/<int:id>/update', methods=['GET', 'POST'])
def update(id):
post = Post.query.get(id)
if request.method == 'GET':
return render_template('update.html', post=post)
else:
post.title = request.form.get('title')
post.body = request.form.get('body')
db.session.commit()
return redirect('/')
更新する際のapp.pyでの記述。
この場合、db.session.commit()だけで良い。(addはいらない)
{% extends "base.html" %}
{% block content %}
<h1>編集画面</h1>
<form method="POST">
<label for="title">タイトル</label>
<input type="text" name="title" value={{post.title}}>
<label for="body">内容</label>
<input type="text" name="body" value={{post.body}}>
<input type="submit" value="更新">
</form>
{% endblock %}
update.htmlもformタグで記述する。
元々入っていた値を表示する際は、inputタグのvalueに{{post.title}}という形で渡す。
記事の削除
@app.route('/<int:id>/delete', methods=['GET'])
def delete(id):
post = Post.query.get(id)
db.session.delete(post)
db.session.commit()
return redirect('/')
記事の削除は、htmlはなし。
<int:id>で削除する記事のidを取得し識別する。そのidをdelete関数の引数に渡すこととPost.query.get()の引数に渡すことをw擦れずに。
db.sessionはdeleteになる。
ターミナル操作
また、新規に作ったデータベースを反映する際は下記のコードをpythonの対話モードで実行する。
from app import db
db.create_all()
既存のデータベースを更新する際は下記のコードをターミナルで実行する。
flask db init
flask db migrate
flask db upgrade
initは最初だけ。
最後に開発モードでローカルでスクリプトを実行するには下記のコードをターミナルで実行
export FLASK_APP=app
export FLASK_ENV=development
flask run
appの部分はアプリの名前