27
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Python+Flaskでウェブアプリケーション④

Last updated at Posted at 2017-02-20
  1. 環境の準備(OSセットアップ)
  2. 環境の準備(OS内のセットアップ)
  3. FlaskのQuickStartの内容をトレースする(インストールと最低限のセットアップ)
  4. FlaskのTutrialの内容をトレースする(基本的なアプリケーションの作り方の学習)★
  5. オリジナルの内容を作る

FlaskのTutrialはこちら。
Flaskrというマイクロブログのサービスを構築する。データベースとしてはsqliteを使用している。

フォルダ構成の作成

アプリケーションを配置するディレクトリを作成する。名前は任意だが、標準に合わせるためにstatic,templatesはそのまま使ったほうがよい。(templatesと複数形にすることを忘れずに)

[root@cnenyuy5l3c ~]# mkdir flaskr
[root@cnenyuy5l3c ~]# cd flaskr
[root@cnenyuy5l3c flaskr]#
[root@cnenyuy5l3c flaskr]# mkdir templates static
[root@cnenyuy5l3c flaskr]#
[root@cnenyuy5l3c flaskr]# virtualenv env
New python executable in /root/flaskr/env/bin/python2.7
Also creating executable in /root/flaskr/env/bin/python
Installing setuptools, pip, wheel...done.
[root@cnenyuy5l3c flaskr]#
[root@cnenyuy5l3c flaskr]# ls
env  static  templates
[root@cnenyuy5l3c flaskr]#

仮想環境分離とFlaskインストール

virtualenvを有効化して、そこにflaskをインストールする。

[root@cnenyuy5l3c flaskr]# . env/bin/activate
(env) [root@cnenyuy5l3c flaskr]#
(env) [root@cnenyuy5l3c flaskr]# pip install flask
Collecting flask
(中略)
Successfully installed Jinja2-2.9.5 MarkupSafe-0.23 Werkzeug-0.11.15 click-6.7 flask-0.12 itsdangerous-0.24
(env) [root@cnenyuy5l3c flaskr]#

DB作成

まずはスキーマを作成。

schema.sql
drop table if exists entries;
create table entries (
  id integer primary key autoincrement,
  title string not null,
  text string not null
);

このスキーマを直接流すわけではなく、Pythonでモジュール化している。
以下のflaskr.pyがflaskrアプリケーション本体になるが、ここではDBの初期化の部分だけ作成している。

flaskr.py
import sqlite3
from flask import Flask, request, session, g, redirect, url_for, \
     abort, render_template, flash
from contextlib import closing    # ここはオフィシャルにはなかったが必要だった

# configuration
DATABASE = '/tmp/flaskr.db'
DEBUG = True
SECRET_KEY = 'development key'
USERNAME = 'admin'
PASSWORD = 'default'

# create our little application :)
app = Flask(__name__)
app.config.from_object(__name__)

def connect_db():
    return sqlite3.connect(app.config['DATABASE'])

def init_db():
    with closing(connect_db()) as db:
        with app.open_resource('schema.sql') as f:  # ここでschema.sqlを実行させる
            db.cursor().executescript(f.read())
        db.commit()

# ここに各種View関数を追記していく。

if __name__ == '__main__':
    app.run(host='0.0.0.0')  # ゲストローカル以外から参照させる

from_objectは対象内の大文字の変数をすべて読み込むというメソッド。
ここでの対象は__name__,すなわち自分自身のファイル(flaskr.py)。
またオリジナルでは最後のapp.runはオプションを指定していないが、今回はホストマシンから参照するために、host='0.0.0.0'を指定している。
これで準備ができたので、DBを作成する。

(flaskr) [root@localhost flaskr]# python
Python 2.7.12 (default, Jan  4 2017, 08:18:28)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-17)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from flaskr import init_db
>>> init_db()
>>>

ここで以下のエラーになる場合は、pythonコンパイル時にsqlite3-develが不足しているため、入れ直す必要あり。

>>> from flaskr import init_db
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "flaskr.py", line 5, in <module>
    import sqlite3
  File "/opt/local/lib/python2.7/sqlite3/__init__.py", line 24, in <module>
    from dbapi2 import *
  File "/opt/local/lib/python2.7/sqlite3/dbapi2.py", line 28, in <module>
    from _sqlite3 import *
ImportError: No module named _sqlite3

作成されたデータベースを一応確認しておく。

(env) [root@cnenyuy5l3c flaskr]# sqlite3 /tmp/flaskr.db
SQLite version 3.6.20
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables
entries
sqlite>
sqlite> .schema
CREATE TABLE entries (
  id integer primary key autoincrement,
  title string not null,
  text string not null
);
sqlite> .quit

View関数の作成

ここから関数を追加していく。最初はデータベースとのコネクション作成とクローズ処理。

@app.before_request
def before_request():
    g.db = connect_db()

@app.after_request
def after_request(response):
    g.db.close()
    return response

gはflaskの提供する特殊なオブジェクトで、現在のデータベースとのconnectionが保存されている。
(gは1つのリクエストの情報しか保存しない)
before_requestはリクエストの前に呼び出される。ここでは何の引数も持たない。

after_requestはリクエストの後に呼び出される。
ここでは、コネクションをクローズし、DBからのレスポンスをクライアントに渡している。
このへんはほぼ決まり文句と考えてよさそう?
ここから実際のページに対応する内容に入る。基本的にはrouteデコレータによって、URLと呼び出される関数を紐づけていく作業。

@app.route('/')    # http://<hostname>:5000/ にアクセスした際に呼び出される関数を定義(ルーティング)
def show_entries():
    cur = g.db.execute('select title, text from entries order by id desc')
    entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
    return render_template('show_entries.html', entries=entries)    # show_entries.htmlというHTMLにentriesという値を埋め込み(レンダリング)

@app.route('/add', methods=['POST'])
def add_entry():
    if not session.get('logged_in'):
        abort(401)
    g.db.execute('insert into entries (title, text) values (?, ?)',
                 [request.form['title'], request.form['text']])
    g.db.commit()
    flash('New entry was successfully posted')
    return redirect(url_for('show_entries'))    # レンダリングでなく、関数に飛ばす場合はredirect()を使用。

@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == 'POST':
        if request.form['username'] != app.config['USERNAME']:
            error = 'Invalid username'
        elif request.form['password'] != app.config['PASSWORD']:
            error = 'Invalid password'
        else:
            session['logged_in'] = True
            flash('You were logged in')
            return redirect(url_for('show_entries'))
    return render_template('login.html', error=error)

@app.route('/logout')
def logout():
    session.pop('logged_in', None)
    flash('You were logged out')
    return redirect(url_for('show_entries'))

HTMLの作成

これで関数(機能)の部分を作成したので、次にHTML(見た目)に移る。
FlaskではおおもとのベースとなるHTML(layout.htmlということが多い)を作り、それを適宜拡張していく形になる。

layout.html
<!doctype html>
<title>Flaskr</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
  <h1>Flaskr</h1>
  <div class=metanav>
  {% if not session.logged_in %}
    <a href="{{ url_for('login') }}">log in</a>
  {% else %}
    <a href="{{ url_for('logout') }}">log out</a>
  {% endif %}
  </div>
  {% for message in get_flashed_messages() %}
    <div class=flash>{{ message }}</div>
  {% endfor %}
  {% block body %}{% endblock %}    <!-- このブロックが参照時に置き換えられる -->
</div>
show_entries.html
{% extends "layout.html" %}    <!-- これで元になるHTMLを指定 -->
{% block body %}    <!-- layout.htmlのbodyというブロックを以下で置き換える -->
  {% if session.logged_in %}
    <form action="{{ url_for('add_entry') }}" method=post class=add-entry>
      <dl>
        <dt>Title:
        <dd><input type=text size=30 name=title>
        <dt>Text:
        <dd><textarea name=text rows=5 cols=40></textarea>
        <dd><input type=submit value=Share>
      </dl>
    </form>
  {% endif %}
  <ul class=entries>
  {% for entry in entries %}
    <li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}
  {% else %}
    <li><em>Unbelievable.  No entries here so far</em>
  {% endfor %}
  </ul>
{% endblock %}
login.html
{% extends "layout.html" %}
{% block body %}
  <h2>Login</h2>
  {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
  <form action="{{ url_for('login') }}" method=post>
    <dl>
      <dt>Username:
      <dd><input type=text name=username>
      <dt>Password:
      <dd><input type=password name=password>
      <dd><input type=submit value=Login>
    </dl>
  </form>
{% endblock %}

CSSの作成

最後にCSSファイルを作成する。

style.css
body            { font-family: sans-serif; background: #eee; }
a, h1, h2       { color: #377BA8; }
h1, h2          { font-family: 'Georgia', serif; margin: 0; }
h1              { border-bottom: 2px solid #eee; }
h2              { font-size: 1.2em; }

.page           { margin: 2em auto; width: 35em; border: 5px solid #ccc;
                  padding: 0.8em; background: white; }
.entries        { list-style: none; margin: 0; padding: 0; }
.entries li     { margin: 0.8em 1.2em; }
.entries li h2  { margin-left: -1em; }
.add-entry      { font-size: 0.9em; border-bottom: 1px solid #ccc; }
.add-entry dl   { font-weight: bold; }
.metanav        { text-align: right; font-size: 0.8em; padding: 0.3em;
                  margin-bottom: 1em; background: #fafafa; }
.flash          { background: #CEE5F5; padding: 0.5em;
                  border: 1px solid #AACBE2; }
.error          { background: #F0D6D6; padding: 0.5em; }

起動、テスト

以上のファイルを以下のように配置する。

  • flaskr/ (ルート)
    • env/
    • templates/
      • layout.html
      • show_entries.html
      • login.html
    • static/
      • style.css
    • flaskr.py
    • schema.sql

ここまでできれば、アプリケーションを起動してアクセスを確認。

(env) [root@cnenyuy5l3c flaskr]# python flaskr.py
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger pin code: 602-691-323

20170131_009.jpg
20170131_010.jpg
20170131_011.jpg

問題なさそう。
これで最低限の基礎は抑えられた(はず)なので、次回以降オリジナルに進む予定です。

27
33
1

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
27
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?