ビジネスパーソン・ガジェット置場 empty lot for business

営業や仕事、それに伴う生活を便利に楽にするツール、ガジェットを作ります。既にあるツールも自分用にカスタマイズ。

python: Flaskの基礎

備忘録です。

FlaskでのWebサービスの作り方をミニマムで。

Flaskで作るWebサービス

必要なライブラリ

from flask import Flask
from flask import render_template, request, redirect
# from flask_bootstrap import Bootstrap
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の部分はアプリの名前