2.Flaskチュートリアルをやってみた(2019/7/30)

# flaskチュートリアルをやってみた
タイトルにもある通りFlaskのチュートリアルを進めたのですが、日本語版チュートリアルは情報が古くうまく動かない部分などがあったため、それらの点を解説しつつブログアプリflaskrを作成していきます。

#step0
チュートリアルではディレクトリ構成を解説しています。
この点は問題はありません。

```
manage.py
requirements.txt
flaskr/
  |- __init__.py
  |- config.py
  |- views.py
  |- models.py
  |- static/
      |- style.css
  |- templates/
      |- layout.html
      |- show_entries.html
```

特筆するまでもないですが、flaskは**templates**以下のhtmlファイルを参照するようにできているのでそれ以外のフォルダ名だと動きません。

##requirements.txt
ここも問題はありません。


```
Flask
Flask-SQLAlchemy
```

以下のコマンドでインストールしてください。

```
pip install -r requirements.txt
```

##manage.py

このファイルを実行することでflaskrというブログアプリを動かします。

```python
from flaskr import app
app.run(host='127.0.0.1', port=5000, debug=True)
```

##flaskr/init.py
ここでは1つ注意する点があります。
チュートリアル通りに作成すると実行した際に以下のようなエラーが起きます。

```
ModuleNotFoundError: No module named 'flask.ext'
```
flask.ext.sqlalchemyの使用はあまり推奨されていないようです。
そのため、2行目を以下のように変更することでこのエラーは解消されます。

```python
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config.from_object('flaskr.config')

db = SQLAlchemy(app)

import flaskr.views
```

## flaskr/config.py


データベースの設定とセッション情報を暗号化するための情報などまとめてconfigfileで管理することができます。

```python
SQLALCHEMY_DATABASE_URI = 'sqlite:///flaskr.db'
SECRET_KEY = 'secret key'
```

# step1
ここからデータベースを作成していくみたいです。

## flaskr/models.py

```python
from flaskr import db

class Entry(db.Model):
    __tablename__ = 'entries'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.Text)
    text = db.Column(db.Text)

    def __repr__(self):
        return '<Entry id={id} title={title!r}>'.format(
                id=self.id, title=self.title)

def init():
    db.create_all()

```
注意点として、上記のコードを書いた段階ではまだデータベースはできていません。
その状態でアプリを起動すると以下のようなエラーが表示されます。

```
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: entries
```
忘れずにターミナルで以下のように打ち込んでデータベースファイルの作成を行なってください。



```
$ python
>>> from flaskr.models import init
>>> init()
```

チュートリアルではここで動作の確認を行なっていますが、これはやってもやらなくても問題ありません。

# step2

ここではブログの一覧と投稿画面を作ります。

## flaskr/views.py

ここも特に問題はありません。チュートリアルの通り進めてください。

```python

from flask import request, redirect, url_for, render_template, flash
from flaskr import app, db
from flaskr.models import Entry

@app.route('/')
def show_entries():
    entries = Entry.query.order_by(Entry.id.desc()).all()
    return render_template('show_entries.html', entries=entries)

@app.route('/add', methods=['POST'])
def add_entry():
    entry = Entry(
            title=request.form['title'],
            text=request.form['text']
            )
    db.session.add(entry)
    db.session.commit()
    flash('New entry was successfully posted')
    return redirect(url_for('show_entries'))
```

# step3

htmlとcss、ブログの機能ではなくて表側をいじっていきます。

##flaskr/template/layout.html

```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>
  {% for message in get_flashed_messages() %}
    <div class=flash>{{ message }}</div>
  {% endfor %}
  {% block body %}{% endblock %}
</div>
```

##flaskr/templates/show_entries.html

```html
{% extends "layout.html" %}
{% block body %}
  <form action="{{ url_for('add_entry') }}" method=post class=add-entry>
    <dl>
      <dt>Title:
      <dd><input type=text size=20 name=title>
      <dt>Text:
      <dd><textarea name=text rows=5 cols=20></textarea>
      <dd><input type=submit value=Share>
    </dl>
  </form>

  <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 %}
```

##flaskr/static/style.css

```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; }
```

あとはチュートリアルの最後にあるように以下のように打ち込むことで問題なく動くと思われます。

```
python manage.py
```

#　自分がつまづいた点

##その１
```
No module named flask
```
上記のようにエラーが出た場合はpythonのバージョンが混在している等の問題がある可能性があります。

非常に基本的な部分なのですがmacだと初期状態でpythonが2.7なのでpython3をデフォルトにする必要がありました。（必須ではないのですがpython3で統一している方が何かと便利なので）

基本的に上のリンクに従って進めて、もしもエラーが出てしまった場合は下のリンクで解決できると思います。

https://qiita.com/sebeckawamura/items/4bc5945245877f250d2e

https://qiita.com/1152v/items/68bb8d1edf10af36bd0d


##その２
```
OSError: [Errno 48] Address already in use
```

上記のエラーはアプリを前回起動した時のプロセス（なんか邪魔なものくらいの認識で大丈夫です）が残っている状態です。アプリを起動している状態でコードをいじったりしてまうとこのようになる可能性があります。

```
$ lsof -i:5000
```
そのためこのようにターミナルで打ち込んで使用中のプロセスを切ってあげましょう。

```
python3.7 23820 kinouchiyuta    3u  IPv4 0xa9d666bd1529b1a1      0t0  TCP localhost:commplex-main (LISTEN)
python3.7 23836 kinouchiyuta    3u  IPv4 0xa9d666bd1529b1a1      0t0  TCP localhost:commplex-main (LISTEN)
python3.7 23836 kinouchiyuta    4u  IPv4 0xa9d666bd1529b1a1      0t0  TCP localhost:commplex-main (LISTEN)
```

このように使用中のプロセスが出てきたら

```
$ kill -9  [該当のID]
```

今回だと以下のように打ち込めば問題ないですね。

```
$ kill -9 23820
$ kill -9 23836
```