【Flask】Configについて
とても参考になったサイト
https://qiita.com/nanakenashi/items/e272ff1aafb3889230bc
https://www.subarunari.com/entry/2018/03/17/いまさらながらFlaskについてまとめる_〜Configuration〜
http://flask.pocoo.org/docs/1.0/config/
https://damyanon.net/post/flask-series-configuration/
Configを使おうというお話。
あなたは何らかのデータベースサーバーやあるいはGoogleSpreadSheetsと連携してプログラムを書いていることでしょう。
書いている内容としてはこんな感じでしょうか。
gss_url = "http://google.****.com"
さて、本番と開発をわけた開発をするとした場合、このgss_url
の値をツドツド変更しているのかもしれません。あるいは、コメントアウトして使っているのかもしれません。
# 本番 # gss_url = 'http://google.****.com' # 開発 gss_url = 'http://test.google.****.com'
ただ、どちらかといえば本番時はhttp://google.****.com
、そうでないときはhttp://test.google.****.com
と条件分岐で指定してあげたいところです。
if HONBAN == True: gss_url = 'http://google.****.com' else: gss_url = 'http://test.google.****.com'
また、Flaskなら開発環境でDEBUG
モードで開発していると思います。本番でDEBUGモードが起きるのはセキュリティ的によくないでしょう。ただ、その複数の切り替えを手動で行っていると絶対忘れが発生して事故るでしょう。
app.run(debug=True) # app.run()
開発環境、本番環境によって変えたい項目は沢山あるはずです。もう一つテスト環境というものがあると思います。
テスト環境はテスト用のデータが挿入された環境であることが望ましいかも知れませんし、あるいはデータそのものが全くない環境が望ましいものかもしれません。
テスト環境1 テスト環境2 開発環境 本番環境
それぞれの状況でアクセスするDBのアドレスやGSSのアドレスが切り替わるような仕組みがあったら便利ですね。今回は環境変数を使って切り替わりを実施できるような仕組みを考えます。
Flaskのデバッグモードとconfigについて
Flaskクラスのインスタンスにはconfig
というものがあります。configはdictionaryとなっています。
app.py
として以下を作成します。
from flask import Flask app = Flask(__name__) print(app.config)
python app.py
として起動すると以下のようになります。
[kuzunoha@:11:09:05:~/sand_box/flaskconfig]$python app.py <Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(0, 43200), 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}>
また、app.pyの3行目を更新してprint(app.config['DEBUG'])
として実行します。
app.py
from flask import Flask app = Flask(__name__) print(app.config['DEBUG'])
実行結果
[kuzunoha@:11:09:10:~/sand_box/flaskconfig]$python app.py False
次にapp.debug = True
を追記してprintしてみます。
from flask import Flask app = Flask(__name__) app.debug = True print(app.config['DEBUG'])
これを実行すると
[kuzunoha@:11:14:58:~/sand_box/flaskconfig]$python app.py True
それではapp.config['Debug'] = Falseと追記してからprintしてみます。
from flask import Flask app = Flask(__name__) app.debug = True app.config['DEBUG'] = False print(app.config['DEBUG'])
これを実行すると
[kuzunoha@:11:17:35:~/sand_box/flaskconfig]$python app.py False
すなわち以下の2つは同義であるということです。
app.debug = Boolean app.config['DEBUG'] = Boolean
また、app.run(debug=True)
で設定するDebugの真偽も同様です。
その他にも環境変数を使用するなどflaskをデバッグモードで実施する方法はあるようです。
参考:http://flask.pocoo.org/docs/1.0/quickstart/#debug-mode
このようにconfig[PARAMETAR]
を変更することで、Flaskアプリケーションのコンフィグを変更できます。
その他のデフォルトのパラメータは公式に載っています。
参考:http://flask.pocoo.org/docs/0.12/config/#builtin-configuration-values
オリジナルのパラメータを作る
flaskのコンフィグパラメータは好きに作ることが出来ます。app.pyを以下のように記載します。
from flask import Flask app = Flask(__name__) app.config['hogehoge'] = 'piyopiyo' print(app.config['hogehoge'])
そして実行すると
[kuzunoha@:11:19:08:~/sand_box/flaskconfig]$python app.py piyopiyo
となります。そして、これはすなわち以下のようにも使えます。
from flask import Flask app = Flask(__name__) app.config['MYSQL_ADDRESS'] = '127.0.0.1' print(app.config['MYSQL_ADDRESS'])
実行すると
[kuzunoha@:11:34:35:~/sand_box/flaskconfig]$python app.py 127.0.0.1
となります。ただ、以下のようにインスタンスとして持ち合わせることは出来ません。
from flask import Flask app = Flask(__name__) app.config['MYSQL_ADDRESS'] = '127.0.0.1' print(app.config.mysql_address) Traceback (most recent call last): File "app.py", line 4, in <module> print(app.config.mysql_address) AttributeError: 'Config' object has no attribute 'mysql_address'
さて、それではその他にもパラメータを追加してMySQLに繋げてみましょう。以下が一例になります。(ほんとにただの一例ですのでこれで実行しても何も起きないです。)
from flask import Flask app = Flask(__name__) app.config['MYSQL_ADDRESS'] = '127.0.0.1' app.config['MYSQL_USER'] = 'root' app.config['MYSQL_PASSWORD'] = 'password' app.config['MYSQL_PORT'] = 3306 app.config['MYSQL_DATABASE'] = 'hogepiyodb' import MySQLdb connection = MySQLdb.connect( host=app.config['MYSQL_ADDRESS'], user=app.config['MYSQL_USER'], passwd=app.config['MYSQL_PASSWORD'], port=app.config['MYSQL_PORT'], db=app.config['MYSQL_DATABASE']) connection.commit() connection.close()
MySQLdb
というモジュールについては以下の参考サイトを見てください。
app.config['MYSQL_ADDRESS'] = '127.0.0.1' app.config['MYSQL_USER'] = 'root' app.config['MYSQL_PASSWORD'] = 'password' app.config['MYSQL_PORT'] = 3306 app.config['MYSQL_DATABASE'] = 'hogepiyodb'
と、このようにパラメータに値を代入していくことが出来るのです。
パラメータの代入方法の選択
Configのパラメータには直接値を代入する方法以外にも方法があります。
参考:https://www.subarunari.com/entry/2018/03/17/いまさらながらFlaskについてまとめる_〜Configuration〜
以下がそれぞれ同じ設定方法になります。ここに記載してる方法以外のものもあります。
ハードコーディング(参考)
上記に書いたものと同じですね。
app.py
from flask import Flask app = Flask(__name__) app.config['DEBUG'] = True app.config['MYSQL_ADDRESS'] = '127.0.0.1' app.config['MYSQL_USER'] = 'root' app.config['MYSQL_PASSWORD'] = 'password' app.config['MYSQL_PORT'] = 3306 app.config['MYSQL_DATABASE'] = 'hogepiyodb' print(app.config['DEBUG']) print(app.config['MYSQL_ADDRESS']) print(app.config['MYSQL_USER']) print(app.config['MYSQL_PASSWORD']) print(app.config['MYSQL_PORT']) print(app.config['MYSQL_DATABASE'])
結果は以下の感じ
[kuzunoha@:12:14:56:~/sand_box/flaskconfig]$python app.py True 127.0.0.1 root password 3306 hogepiyodb
from_pyfile
cfgファイルを読み込むタイプです。app.pyと同じディレクトリにdevelopment.cfg
というcfgファイルを作って、それを読み込むようにします。
development.cfg
DEBUG=True MYSQL_ADDRESS='127.0.0.1' MYSQL_USER='root' MYSQL_PASSWORD='password' MYSQL_PORT=3306 MYSQL_DATABASE='hogepiyodb'
app.py
from flask import Flask app = Flask(__name__) app.config.from_pyfile('development.cfg') print(app.config['DEBUG']) print(app.config['MYSQL_ADDRESS']) print(app.config['MYSQL_USER']) print(app.config['MYSQL_PASSWORD']) print(app.config['MYSQL_PORT']) print(app.config['MYSQL_DATABASE'])
app.py
の3行目app.config.from_pyfile('development.cfg')
に注目です。
app.config.from_pyfile(CFGファイル名)となっております。
実行結果は以下の通り
[kuzunoha@:12:29:10:~/sand_box/flaskconfig]$python app.py True 127.0.0.1 root password 3306 hogepiyodb
from_object
設定のクラスが書かれたPythonファイルを読み込むようにします。
setting.py
class Development(object): DEBUG=True MYSQL_ADDRESS='127.0.0.1' MYSQL_USER='root' MYSQL_PASSWORD='password' MYSQL_PORT=3306 MYSQL_DATABASE='hogepiyodb'
app.py
from flask import Flask app = Flask(__name__) app.config.from_object('setting.Development') print(app.config['DEBUG']) print(app.config['MYSQL_ADDRESS']) print(app.config['MYSQL_USER']) print(app.config['MYSQL_PASSWORD']) print(app.config['MYSQL_PORT']) print(app.config['MYSQL_DATABASE'])
実行結果は以下の通り
[kuzunoha@:12:37:18:~/sand_box/flaskconfig]$python app.py True 127.0.0.1 root password 3306 hogepiyodb
app.py
の3行目app.config.from_object('setting.Development')
に注目です。
app.config.from_object('Pythonファイル名.オブジェクト名')となっていることがわかると思います。
オブジェクトを取得するようになっているので、setting.pyに新しいクラスを作って読み込ませることも出来ます。クラスなので継承を使って新しいクラスを作っても良いです。
setting.py
class Development(object): DEBUG=True MYSQL_ADDRESS='127.0.0.1' MYSQL_USER='root' MYSQL_PASSWORD='password' MYSQL_PORT=3306 MYSQL_DATABASE='hogepiyodb' class Production(Development): DEBUG=False MYSQL_ADDRESS='63.31.125.212'
app.py
from flask import Flask app = Flask(__name__) app.config.from_object('setting.Production') print(app.config['DEBUG']) print(app.config['MYSQL_ADDRESS']) print(app.config['MYSQL_USER']) print(app.config['MYSQL_PASSWORD']) print(app.config['MYSQL_PORT']) print(app.config['MYSQL_DATABASE'])
実行結果はこうなりました
[kuzunoha@:12:54:12:~/sand_box/flaskconfig]$python app.py False 63.31.125.212 root password 3306 hogepiyodb
DEBUG
とMYSQL_ADDRESS
の値が変更されていることと、それ以外は継承元のそれと同じであるというところが注目点です。
from_pyfile + from_object
ではこの2つを合体して使ってみるとどうでしょうか
password.cfg
MYSQL_PASSWORD='heavensdoor'
setting.py
class Development(object): DEBUG=True MYSQL_ADDRESS='127.0.0.1' MYSQL_USER='root' MYSQL_PASSWORD='password' MYSQL_PORT=3306 MYSQL_DATABASE='hogepiyodb' class Production(Development): DEBUG=False MYSQL_ADDRESS='63.31.125.212'
app.py
from flask import Flask app = Flask(__name__) app.config.from_object('setting.Production') app.config.from_pyfile('password.cfg') print(app.config['DEBUG']) print(app.config['MYSQL_ADDRESS']) print(app.config['MYSQL_USER']) print(app.config['MYSQL_PASSWORD']) print(app.config['MYSQL_PORT']) print(app.config['MYSQL_DATABASE'])
実行結果は以下の通りです。
[kuzunoha@:13:10:48:~/sand_box/flaskconfig]$python app.py False 63.31.125.212 root heavensdoor 3306 hogepiyodb
最終的なMYSQL_PASSWORD
はheavensdoor
になりました。app.py
の3-4行目を見てみてください。
app.config.from_object('setting.Production') app.config.from_pyfile('password.cfg')
変数のそれと同じく、最後に宣言された値に上書きされていくからです。
環境変数を使ってそれぞれの環境にConfを変える
from_objectで読み込むクラスを変更すれば各環境ごとに使うconfigを変更できます。
# 開発なら app.config.from_object('setting.Development') # 本番なら app.config.from_object('setting.Production')
辞書型を使うともう少しスマートになります。
dict_confmode = { 'dev' : 'setting.Development', 'pro' : 'setting.Production' } # 開発なら confmode = dict_confmode['dev'] # 本番なら confmode = dict_confmode['pro'] app.config.from_object(confmode)
さらに環境変数を使います。例えばFLASK_CONFIGURATION
という環境変数名に開発ならdev
、本番ならpro
と入れておきます。
---開発環境なら---
環境変数
FLASK_CONFIGURATION=dev
app.py
の一部
import os dict_confmode = { 'dev' : 'setting.Development', 'pro' : 'setting.Production' } env_confmode = os.getenv('FLASK_CONFIGURATION') confmode = dict_confmode[env_confmode] app.config.from_object(confmode)
---本番環境なら---
環境変数
FLASK_CONFIGURATION=pro
app.py
の一部
import os dict_confmode = { 'dev' : 'setting.Development', 'pro' : 'setting.Production' } env_confmode = os.getenv('FLASK_CONFIGURATION') confmode = dict_confmode[env_confmode] app.config.from_object(confmode)
それぞれのapp.pyには全く違いはありません。この通り、PCに宣言した環境変数だけで環境を変更できるようになりました。
参考:https://wa3.i-3-i.info/word11027.html
共有することがあまりよろしくない値はfrom_pyfileへ
DBのpasswordや暗号方法、暗号キーやアクセストークンなどはcfg
ファイルへ記載するようにします。
そうしてinstance
ディレクトリを作ってそのcfg
ファイルを置いておくようにします。
アプリケーションを初期化する際にinstance_relative_config=True
という引数を当ててあげます。そうすることでinstance
というディレクトリからcfg
ファイルを読みに行くようになります。
app = Flask(__name__, instance_relative_config=True)
そうして、.gitignore
にinstance
を入れてあげます。
最終的な構成はこのようになりました。
app.py setting.py instance/ ┗ password.cfg
password.cfg
MYSQL_PASSWORD='heavensdoor'
setting.py
class Development(object): DEBUG=True MYSQL_ADDRESS='127.0.0.1' MYSQL_USER='root' MYSQL_PASSWORD='password' MYSQL_PORT=3306 MYSQL_DATABASE='hogepiyodb' class Production(Development): DEBUG = False MYSQL_ADDRESS='63.31.125.212'
app.py
import os from flask import Flask app = Flask(__name__, instance_relative_config=True) dict_confmode = { 'dev' : 'setting.Development', 'pro' : 'setting.Production' } env_confmode = os.getenv('FLASK_CONFIGURATION') confmode = dict_confmode[env_confmode] app.config.from_object(confmode) app.config.from_pyfile('password.cfg') print(app.config['DEBUG']) print(app.config['MYSQL_ADDRESS']) print(app.config['MYSQL_USER']) print(app.config['MYSQL_PASSWORD']) print(app.config['MYSQL_PORT']) print(app.config['MYSQL_DATABASE'])
実行結果
[kuzunoha@:14:38:21:~/sand_box/flaskconfig]$export FLASK_CONFIGURATION=dev [kuzunoha@:14:38:30:~/sand_box/flaskconfig]$python app.py True 127.0.0.1 root heavensdoor 3306 hogepiyodb [kuzunoha@:14:38:32:~/sand_box/flaskconfig]$export FLASK_CONFIGURATION=pro [kuzunoha@:14:38:43:~/sand_box/flaskconfig]$python app.py False 63.31.125.212 root heavensdoor 3306 hogepiyodb
参考 Windowsの環境変数設定:https://www.k-cube.co.jp/wakaba/server/environ.html
参考 Unix,Linux,Macの環境変数設定:https://qiita.com/iam1at/items/91cb8478160c9fbee134
残りはベストプラクティスを見てみてください。
細かいところは違いますが、以下のURLの内容がよくわかるようになったと思います。後はここを読んでください。