必要なPythonModule

requirements.txtに記載されていますので確認してください。

MVCモデルで開発をするということ

Model
View
Controller

それぞれの頭文字をとってMVCと呼ばれます。

参考[MVCについて]:https://qiita.com/gcyata/items/772b6989f4cdab0bd047

ディレクトリ構成

カレントディレクトリ
│  .gitignore
│  README.md
│  requirements.txt
│
├─test
│  │  conftest.py
│  │
│  ├─flasktest
│  │      test_app.py
│  │
│  └─modeltest
│         test_Databases.py
│
└─www
   │  app.py
   │  factory_app.py
   │  setting.py
   │
   ├─controllers
   │      posts.py
   │      __init__.py
   │
   ├─insance
   │      sample_application.cfg
   │
   ├─models
   │      Databases.py
   │      __init__.py
   │
   ├─model_instance
   │      database.py
   │      __init__.py
   │
   ├─templates
   │  └─posts
   │          index.html
   │
   └─views
           posts.py
           __init__.py

wwwディレクトリ

実行するFlaskWebアプリケーションはこちらに配置します。

app.py

flaskの実行ファイルになります。

factory_app.create_app関数をインポートし、その返り値を変数appに代入しています。create_app関数はFlaskの実行ファイルに色々な設定を行って返します。その返り値を受け取ったappapp.run()とすることでWebアプリケーションが起動します。

create_app関数に環境ステータスであるdevtestprodのいずれかを渡すようにします。

なお、os.getenv()を使って環境変数から取得するようにするのもよいでしょう。

このapp.pycreate_appを通して沢山のmoduleにアクセスしています。ルートパスはこのapp.pyを起点としてインポートしてあげてください。

factory_app.py

Application Factoryの概念は以下のURLに載っています。

http://flask.pocoo.org/docs/1.0/patterns/appfactories/

# Flask実行ファイル読込
app = Flask(__name__, instance_relative_config=True)

こちらはFlaskのインスタンスを作成している場面です。instance_relative_config=Truecfgファイルの置き場所をinstanceディレクトリに設定するというものです。本来はcfgファイルについて同じディレクトリ内のものを読み込むのですが、この引数を設定することでinstanceディレクトリ内のものを読み込むように変更することが出来ます。また、instance_path='/path/to/instance/folder'という引数に絶対PATHを渡してインスタンスディレクトリのパスと名前を変更することも可能です。

参考[インスタンスフォルダ]:http://flask.pocoo.org/docs/1.0/config/#instance-folders

# コンフィグ読込
confmode = dict_confmode[config_mode]
app.config.from_object(confmode)
app.config.from_pyfile('application.cfg', silent=True)

コンフィグファイルの読込箇所になります。

app.config.from_object(confmode)は辞書dict_confmodeのValueを使ってsetting.pyに書き込まれているクラスを読み込んでいます。

app.config.from_pyfile('application.cfg', silent=True)はinstanceディレクトリ内のapplication.cfgを読み込みます。センシティブな内容をこちらに入れるようにしてください。なお、application.cfg.gitignoreに記載しており、GitHub上にはPushされないようにしてあります。

silent=Trueとは、instanceディレクトリ内にapplication.cfgがない場合は、処理を無視するというものになります。

上記二つの設定ファイルにおいて、同じ値が存在する場合は、後で呼ばれたほうが上書きするという性質を忘れないでください。それ以外の値は、そのままapp.configに代入されます。

# app.config.from_object(confmode)
A = 1
B = 2
C = 3
# app.config.from_pyfile('application.cfg', silent=True)
C = 4
D = 5
# 最終的な値
app.config['A'] = 1
app.config['B'] = 2
app.config['C'] = 4
app.config['D'] = 5

参考[Flask設定ファイルについて]:https://qiita.com/nanakenashi/items/e272ff1aafb3889230bc

from controllers.posts import post_ctr
~~~~~~
# コントローラ読込
app.register_blueprint(post_ctr)

from controllers.posts import post_ctrはMVCのコントローラーに該当するプログラムです。

参考[MVCについて]:https://qiita.com/s_emoto/items/975cc38a3e0de462966a

app.register_blueprint(post_ctr)は上記のpost_ctr.pyをコントローラとして読込を行っています。

app.register_blueprint(post_ctr, url_prefix='/posts')とようにurl_prefix引数に値を入れると、その文字列がURLのプレフィックスとなります。

参考[prefixとは]:https://wa3.i-3-i.info/word1208.html

from model_instance import database
~~~~~~
# モデルインスタンス初期化
database.init_db(app)

from model_instance import databaseは初期設定が必要で使いまわしたいクラスがインスタンス化された変数が格納されているPythonプログラムです。

database.init_db(app)database.py内のインスタンスに初期設定を行う関数です。(ちょっと難しいかもです。)

Flask-SQLAlchemyといったものがこの方式で初期化可能です。

参考[Flask-SQLAlchemy]:https://qiita.com/shirakiya/items/0114d51e9c189658002e

setting.py

Flaskの設定用プログラムです。

app.config[***]の部分を設定します。InitConfを継承している点をよく見ていてください。

SQL_TABLE = 'hogehoge_table'とするとapp.config['SQL_TABLE']'hogehoge_table'になります。

また、SECRET_KEYを設定すればapp.secret_keyの値が決まります。その他、色々と設定用の値が決められています。

参考[ビルトインコンフィグバリュー]:http://flask.pocoo.org/docs/1.0/config/#builtin-configuration-values

controllersディレクトリ

MVCモデルのコントローラープログラムを置いておくディレクトリになります。factory_app.pyapp.register_blueprint(post_ctr, url_prefix='/posts')の項目で説明した通り、URLプレフィックス単位でコントローラープログラムを変更したいといった場合は、新しいコントローラープログラムを作成し、このディレクトリにおいてください。

posts.py

これはpostsコントローラープログラムになります。

from flask import Blueprint ...
post_ctr = Blueprint('post_ctr', __name__)

これはport_ctr変数がBlueprintクラスのインスタンスとなっています。以降はport_ctrが持つデコレータ@post_ctr.routeを使ってURLのコントロールを行います。

参考[Blueprintについて]:http://flask.pocoo.org/docs/1.0/blueprints/

from views.posts import index_page ...
~~~~~~
@post_ctr.route('/')
def index():
    return index_page()

これはhttp://IPADDRESS_or_DOMAINNAME/にアクセスされたときにindex()関数が実行されるというデコレータになります。factory_app.pyの項目でapp.register_blueprint(post_ctr, url_prefix='/posts')としたようにurl_prefix='/posts'と引数を与えていた場合は,http://IPADDRESS_or_DOMAINNAME/posts/にアクセスした際に実行されます。

return index_page()はimportされている通り、viewsディレクトリposts.pyindex関数になります。詳しくはviewsディレクトリの項目を読んでください。

@post_ctr.route('/a/<post>')
def argstest(post):
    return argstest_page(post)

これはhttp://IPADDRESS_or_DOMAINNAME/a/some_value/にアクセスされたときにargstest(post)関数が実行されるというデコレータになります。factory_app.pyの項目でapp.register_blueprint(post_ctr, url_prefix='/posts')としたようにurl_prefix='/posts'と引数を与えていた場合は,http://IPADDRESS_or_DOMAINNAME/posts/a/some_value/にアクセスした際に実行されます。

これはurlの個所に<post>という値が来ていることをよく見てください。これはURL変数といって、この個所に書き込まれた内容を関数に引き渡すことが出来ます。例ではsome_valueという文字列がURL変数postに代入されています。

最終的にはargstest('some_value')として実行されています。

詳しくは参考を見てください。

参考[variable-rules]:http://flask.pocoo.org/docs/1.0/quickstart/#variable-rules

なお、とてもどうでもいい話ですが、Blueprintという言葉は青図といって元々は建築物の設計図が書かれた用紙が青色だったことが名前の由来のようです。下記URLにも記載の通り英語のブループリントと言う言葉は今日も多種様な業界で使われているということを覚えておくとよいでしょう。(要するにUnrealEngineというゲームを作るフレームワークにもBlueprintっていうモノがあるのでごちゃごちゃにならないように注意してねってことです。)

参考[青図]:https://ja.wikipedia.org/wiki/%E9%9D%92%E5%9B%B3

instanceディレクトリ

factory_app.pyの説明を読んでください。また、sample_application.cfgを元にapplication.cfgを作成してください。

model_instanceディレクトリ

初期化が必要なインスタンスを宣言しておくプログラムを入れておくディレクトリになります。factory_app.pyにて初期設定を行い、viewmodelのプログラムからここを呼び出して使ってあげてください。

database.py

これは例のためのプログラムであり、実際はなんら動作しません。

from models.Databases import HyperDatabase
db = HyperDatabase()

これはmodelsディレクトリのDatabases.py内に宣言されたHyperDatabaseクラスを呼び、そのクラスのインスタンスがdbとなっています。

app.pyが最初に実行するfactory_app.pyが呼び出されたとき、factory_app.pyのimport文としてfrom model_instance import databaseと記載があるため、このdb = HyperDatabase()まで実行されるということを覚えておいてください。

def init_db(app):
    db.init_app(sql_address=app.config['SQL_ADDRESS'])

これは、HyperDatabaseのインスタンスであるdbに対し、初期設定をおこなっています。

上記をまとめると、factory_app.pyが呼び出されただけでdbインスタンスが作成され、create_app関数内で、このinit_db関数が実行され、dbに初期設定が反映されるということになります。

そうして、各モデル内で"初期設定が反映されたdbインスタンス"を呼び出すことが可能となっているのです。

あんまり参考にならないかもしれない参考[db.init_appの初期化処理]:https://teratail.com/questions/175199

modelsディレクトリ

MVCモデルのモデルプログラムを置いておくディレクトリになります。要するにクラスを入れといてください。

Databases.py

class HyperDatabase(object):
    def init_app(self, sql_address):
    ...

初期化が必要であれば、初期化用のクラス内関数init_appを作っておくとよいでしょう。そうして、model_instanceで呼び出すようにしてあげてください。

def init_app(self, sql_address):
    if type(sql_address) != str:
        raise TypeError(...)
    else:
        self.database = sql_address

init_app関数の中身は要約するとこうなっています。単純な話、引数が文字列じゃない場合はTypeErrorというエラーを返していて、そうでない場合はインスタンス変数databaseに引数の値を渡しているわけです。

参考[raiseについて]:https://docs.python.org/ja/3/tutorial/errors.html#raising-exceptions

templatesディレクトリ

テンプレートエンジンが使用するテンプレートとなるhtmlを置く場所になります。

コントローラ単位で複数ディレクトリを作成しております。

参考[テンプレートエンジンとは]:http://lovee7.blog.fc2.com/blog-entry-95.html

postディレクトリ

コントローラを複数に設定している場合は、コントローラ単位でディレクトリを構築してあげてもよいでしょう。こうしないとプログラムが動かないというわけではないです。もっといい方法があれば変えていきましょう。

posts.index.html

htmlについては参考を…

参考[htmlについて]:https://udemy.benesse.co.jp/development/web/what-is-html.html

{{ db.database }}と記載がある部分に関して、htmlと違った個所になるかと思います。viewsディレクトリのposts.pyの以下のコードを見てください。

def index_page():
    return render_template('posts/index.html', db=db)

render_template関数の引数としてdb=dbしておりますが、この左側のdb{{ db }}として,html内で呼び出されます。hoge=dbとすれば、{{ hoge }}としてあげる必要があります。

また、テンプレートエンジン内でif文for文も使うことが出来ます。使い方は{% if %}{% endif %}といったものなのですが、普段のpythonとはちょっと違うので以下のURLをよく見てください。

参考[Flaskとjinja2]:https://qiita.com/bookun/items/7ae5de21307d101b4759

viewsディレクトリ

MVCモデルのビュープログラムを置いておくディレクトリになります。主にcontrollersディレクトリ内のposts.pyから呼び出されるようにします。

posts.py

from flask import render_template
~~~~~
def index_page():
    return render_template('posts/index.html', ...)

from flask import render_templateflaskjinja2というテンプレートエンジンを呼び出しています。

render_template('posts/index.html')はテンプレートとなるhtmlファイルのパスを指定しています。

from model_instance.database import db
~~~~~
def index_page():
    return render_template(..., db=db)

from model_instance.database import dbmodel_instanceディレクトリの項目をよく見てください。要するに初期設定が反映されたdbを使用しています。

(db=db)は左辺が、テンプレートエンジン内で使用する変数名となり、右辺がこのpage.py内にある変数や値になります。左辺は好きな名前を使用できます。testディレクトリ項目のposts.index.htmlの項目をよく見てください。

細かいけど注意しておいてほしいこと…

Flaskはテンプレートエンジンであるjinja2を内蔵してありますが、jinja2Flask内蔵のモノと単品のモノがそれぞれ存在するということを覚えておいてください。そのため、flaskjijna2について何らかの設定を行うときと単品のjijna2の設定を行うときと、設定の仕方が若干異なるということを覚えておいてください。

testディレクトリ

pytestを実行するためのディレクトリです。

参考[pytest]:https://qiita.com/everylittle/items/1a2748e443d8282c94b2

pytestを実行するには、このディレクトリの最上部かtestディレクトで、CLIにてpytestと打つかpython -m pytestと打ちましょう。

conftest.py

pytestが呼ばれたときに一番最初に読み込まれるファイルです。

import os
import sys
sys.path.append(os.path.abspath(os.path.dirname(
    os.path.abspath(__file__)) + "/../www/"))

これはpytestを実行するディレクトリを設定しています。このようにすることで、wwwディレクトリは、このテストプログラムのことを気にせずにディレクトリを配置出来たり、ディレクトリの読込みが出来ます。

参考[pytestのPATHについて]:https://www.magata.net/memo/index.php?pytest%C6%FE%CC%E7

flasktest.test_app.py

flaskアプリのテストを行います。app.test_client()等を使ったテストが主になります。

参考[app.test_client()について]:http://momijiame.tumblr.com/post/39324429279/python-%E3%81%AE-flask-%E3%81%A7%E4%BD%9C%E3%81%A3%E3%81%9F%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E3%83%86%E3%82%B9%E3%83%88%E3%81%99%E3%82%8B

modeltest.test_Databases.py

Modelのテストを行います。自作のモデルを呼び出すようにします。パスはwwwが起点です。

参考[pytestについて]:https://dev.classmethod.jp/server-side/python/pytest-getting-started/