Kuzunoha-NEのブログ

プログラミングなどの勉強をしてます

【Python】【Flask-Migration】 flask db init でKeyError: 'migrate'って表示された。

こんばんは、葛の葉です。

flask-migrationを使っていて、コマンドでflask db initと実行したらKeyError: 'migrate'と表示されて実行されないということがありました。

File "/usr/local/lib/python3.6/site-packages/flask_migrate/__init__.py", line 125, in init
directory = current_app.extensions['migrate'].directory
KeyError: 'migrate'

環境

Python 3.6.6

Flask 1.0.2

Flask-Migrate 2.3.2

flask db initをする前に

flaskの起動方法について、認識をちょっと改める必要があって、そもそもflaskを起動するには以下のようにするのがお約束のようです。

flask run

python [flaskが記述されたpythonファイル]では正しい方法では無いようです。

flask.pocoo.org

それで、flaskは環境変数FLASK_APPに入っているpyファイルを読み込む設定のようで、環境変数FLASK_APPに中身が存在しない場合は、app.pyを読み込むようになっているようです。hello.pyにflaskのコードがある場合は以下のようにします。

export FLASK_APP=hello.py
flask run

flask-migrateのコマンドflask init dbとする場合もFLASK_APPが該当のものでないと駄目のようです。

stackoverflowで見てみる

stackoverflow.com

from flask_migrate import Migrate
migrate = Migrate(app, db) 

こうしなさいと書いてあった。

結局こうなった

# app.py

from flask_migrate import Migrate

from factory import create_app
from objects.database import db

app = create_app()
migrate = Migrate(app, db)

if __name__ == '__main__':
    app.run()

app.pyがおいてる場所をカレントディレクトリにして、flask db initが動いた。

上記のインポート先のコード

# factory.py

from flask import Flask
from objects.database import db

def create_app():
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = 'hogehoge'
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
    db.init_app(app)
    return app
# objects/database.py
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def init_db(app):
    db.init_app(app)

【Oracle VirtualBox】ブリッジ設定を使ってホストマシンのローカルネットワークからゲストマシンにアクセスする

こんにちは、葛の葉です。

ホストマシンのローカルネットワーク環境からゲストマシンにアクセスする方法を記載します。

環境

Oracle VirtualBox 5.2.22
Host  Windows 10 Home
Guset Ubuntu 16.04

ブリッジ設定をする

VirtualBoxの画面でアクセスしたいゲストマシンを選択した状態で設定ボタンを押下します。左タブのネットワークを選択し、アダプター1の割り当てのプルダウンメニューをブリッジアダプターにして、名前のプルダウンメニューを現在のローカルネットワークに接続している"ホストマシンのネットワークアダプタ"にします。

f:id:Kuzunoha-NE:20190308121307j:plain

次にゲストマシンを起動してShellで以下のコマンドを打ちましょう。

ip address 

また、ip aでもいいです。自動補完してaaddressに変換されるっぽいので。(Aliasかも?)

そうすると、どこかのインターフェースでホストマシンと同じネットワークに繋がっているアドレスが存在しているのでメモってください。(この辺ざっくりしててごめんなさい🙇)これがこのゲストマシンにアクセスするためのIPアドレスになります。

f:id:Kuzunoha-NE:20190308123410j:plain

アクセスしてみる

例えばそのゲストマシンにNginxを導入していたら、192.168.1.?1にブラウザでアクセスするとWelcome to nginx!がちゃんと表示されているはず。

f:id:Kuzunoha-NE:20190308124637j:plain

同じネットワーク内ならスマホからでもアクセス可能です。

【Python】Flaskでcreate_app関数を作って使う

こんばんは、葛の葉です。

今回はFlaskで調べものをしているとよく見かけるcreate_app関数を使ってみます。

環境

Python 3.6.6

Flask 1.0.2

ディレクトリ構成はこちら

カレントディレクトリ
├── app_factroy.py
├── app.py
└── test_app.py

create_appを作る

# app_factroy.py

from flask import Flask

def create_app(config_mode='test'):
    app = Flask(__name__)
    if config_mode == 'test':
        app.config['DEBUG'] = True
        app.config['SQL_ADDRESS'] = '127.0.0.1'
    else:
        app.config['DEBUG'] = False
        app.config['SQL_ADDRESS'] = '69.196.1.1'
    return app

create_app関数はFlask(__name__)インスタンスであるappに様々なコンフィグを与え、最終的にそのappを返却します。create_app関数の引数config_modeには開発環境か、テスト環境か、本番環境か、といった環境のステータスを渡してあげます。

create_appを呼び出す

# app.py

from app_factroy import create_app

app = create_app('dev')
# import os
# app = create_app(os.getenv('FLASK_CONFIGURE_MODE'))

# 以下何らかの処理

if __name__ == __main__:
    app.run()

create_app関数を呼び出して、引数に環境情報を与えます。返り値をapp変数に代入します。そうしてapp.run()してあげれば、Flaskは実行されます。

また、os.getenvを使って環境変数から環境情報を与えるのもありかなぁと思います。今回のコメントアウト部分がそれです。FLASK_CONFIGURE_MODEという環境変数内にtestとか入れておくと、create_appの引数にtestが入ります。いいねぇ。

下記の記事を参考にベストプラクティスなコンフを作ると良いのでは、と思います。というか、私はそうやってます。ありがとうございます。

qiita.com

create_appでtestする

# test_app.py

from app_factroy import create_app

app = create_app('test')

assert '127.0.0.1' == app.config['SQL_ADDRESS'] 

こんな感じで呼び出してテストしてあげましょう。実行したら何も起こらずにコマンドラインが返ってくると思います。(なんかあったらテスト落ちてるって意味です。)

【Python】配列コピーについて

こんばんは、葛の葉です。

さて、Pythonの配列のコピーについて、書きます。

環境

Python3.6.6

コード達

Python 3.6.6 (default, Sep  5 2018, 03:40:52)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = [1,2,3,4,5]
>>> b = a
>>> a
[1, 2, 3, 4, 5]
>>> b
[1, 2, 3, 4, 5]
>>> a[0] = 6
>>> a
[6, 2, 3, 4, 5]
>>> b
[6, 2, 3, 4, 5] #←b[0]が変更されている
>>> id(a)
140405585847624
>>> id(b)
140405585847624
>>> id(a) == id(b)
True
#IDの値は全く同じである。

配列の中身をコピーしているわけではなく、配列として参照しているアドレスをコピーしているため。

配列をコピーしたい場合

import copyを用いたコピー

c配列

>>> import copy
>>> c = copy.deepcopy(a)
>>> id(c)
140405578255624
>>> id(a) == id(c)
False #<- IDが異なる
>>> c
[6, 2, 3, 4, 5]
>>> c[0] = 7
>>> c
[7, 2, 3, 4, 5]
>>> a
[6, 2, 3, 4, 5]
>>> a
[6, 2, 3, 4, 5]
>>> b
[6, 2, 3, 4, 5]
>>> c
[7, 2, 3, 4, 5]
>>> a[0] = 8
>>> a
[8, 2, 3, 4, 5]
>>> b
[8, 2, 3, 4, 5]
>>> c
[7, 2, 3, 4, 5]

スライス[:]を用いたコピー

d配列

>>> d = a[:]
>>> d
[8, 2, 3, 4, 5]
>>> d[0] = 9
>>> d
[9, 2, 3, 4, 5]
>>> a
[8, 2, 3, 4, 5]
>>> id(d)
140405578255688
>>> id(d) == id(a)
False
>>> id(d) == id(a)
False

結論

リストをコピーするときは

import copy
new_array = copy.deepcopy(array)

または

new_array = array[:]

【Discord】Botから役職へのメンション方法

こんばんは、葛の葉です。

ちょっと前に会社でDiscord使ってる旨お伝えしたかと存じますけど、そんな中でDiscord内の役割(権限)にメンションを飛ばしたくなりました。それが意外と日本語に乗っていなかったのでこちらに記載します。

役職にメンション飛ばすんだって

実は以下に記載の通り。でも、最初見たときよくわからなかったのでざっくりと説明すると役割に一回特殊なメンション飛ばすと、その飛ばしたときの書き込みがちょっと変わった文字<@&????????????>みたいなものになるので、Botを使うときはその<@&????????????>を使うようにします。

discordhelp.net

やってみた

まず役割の設定をします。サーバーを右クリックしてサーバー設定→役割をクリック。

f:id:Kuzunoha-NE:20190306184544j:plain

飛ばしたい役職を選択してこの役職に対して@メンションを許可するをオンにして保存

f:id:Kuzunoha-NE:20190306184851j:plain

どこでもいいので\@<役職名>と送信する。今回は\@ハンプティダンプティそうすると送信された文字がちょっと変わった物になっている。

f:id:Kuzunoha-NE:20190306185244j:plain

<@$????????????????>みたいなものが出ていると思うので、これをそのまま書き込めばその役職へのメンションになる。

botなんかに

botなんかで役職に送信したい場合は<@$????????????????>を文字列に付け加えてあげればOKなはず。

【GAS】Googleフォームで入力してもらった値をLogger.logに出力する

こんばんは、葛の葉です。

さて、Googleフォームという大変便利がいいWebアプリがありますね。

www.google.com

これとGASを連携し、GAS上のLogger.logに出力することが出来ます。

こんなGoogleフォームです

f:id:Kuzunoha-NE:20190214115619j:plain

問い合わせ内容の中身をGASで取得するようにします。

スクリプトエディタを開く

先ずはスクリプトエディタを開きます。開き方はGoogleフォームのエディタモードから右上のその他のボタン(黒点が縦に三つ並んでいるボタン)をクリックして、出てきたリストからスクリプト エディタをクリックしてください。

f:id:Kuzunoha-NE:20190214120204j:plain

そうしたらスクリプトエディタ画面に移行するはずです。下記のようなコードが記載されていると思います。

function myFunction() {
  
}

コードの記入

以下のように記入し、保存してください。(Ctrl + s)など。

function myFunction(event){
  var values = event.response.getItemResponses();
  var value = values[0].getResponse();
  Logger.log(value)
}

初めて保存する際はプロジェクトの名前を問われますので、お好みで。

トリガーの設定

スクリプトエディタ画面で「現在のプロジェクトのトリガー」ボタン(時計の形をしたボタン)があります。

f:id:Kuzunoha-NE:20190214122619j:plain

それをクリックしてください。そうしますと、G Suite Developper Hubというページに飛びます。右下の「トリガーを追加」ボタンをクリックします。

f:id:Kuzunoha-NE:20190214122702j:plain

実行する関数を選択myFunctionになっていることを確認した後、下部にスクロールした後イベントの種類を選択フォーム送信時に変更してください。

f:id:Kuzunoha-NE:20190214122841j:plain

保存をすればGoogleアカウントのことを聞かれますので、そこの対応をお願いします。

問題なければここで製作は完了しています。

実際にフォームに入れてみる。

f:id:Kuzunoha-NE:20190214121658j:plain

こんな内容のものを送信します。

スクリプトエディタに移動して表示-> ログとしてみてください。

f:id:Kuzunoha-NE:20190214121900j:plain

このようにログに出力されています。

Loggerlogの部分を変えてみましょう

例えば以前作成したDiscordに送信するための関数に渡してもよいでしょう。その他のチャットツールとかでも使えるんじゃないかなぁって思います。

kuzunoha-ne.hateblo.jp

kuzunoha-ne.hateblo.jp

【Python】受け取った配列に対し、Keyが1,2,3,4と連番した数字になるような辞書を返す関数 + Unittest

こんばんは、葛の葉です。

引数に配列を渡すと、連番をKeyにした辞書型を返してくれる関数を作りました。必要かどうかわかんないけど、一時、入用だったのですが、多分もういらない関数なのでここで供養させてください。

こんなんなのよ

関数numbering_dictionary()の引数に['nezumi', 'ushi', 'tora', 'usagi']のような配列を渡すと{1: 'nezumi', 2: 'ushi', 3: 'tora', 4: 'usagi'}という辞書を返します。だからなんだってレベルのものですな。

関数はこんな感じ

helper_def.pyという名前で以下のコードを保存してください。

def numbering_dictionary(arry_choices_answer, start_key=1):
    '''
    受け取った配列に対し、Keyが1,2,3,4と連番した数字になるような辞書を返します。
    ex: numbering_dictionary(['nezumi','ushi','tora'])
        -> {1: 'nemuzi', 2: 'ushi', 3: 'tora'}
    start_key(int)は連番開始の数字になります。デフォルトは1。
    '''
    numbring_dictionary = {}
    for index_number, choices_answer in enumerate(arry_choices_answer, start=start_key):
        numbring_dictionary[index_number] = choices_answer
    return numbring_dictionary

Unittestはこんな感じ

Unittest用プログラミングは以下になります。test_helper_def.pyみたいな名前でhelper_def.pyと同じところに保存してください。なお、動作させるだけならこのファイルは要らないです。

import pytest

from . import helper_def

def test_numbering_dictionary():
    array1 = ['nezumi', 'ushi', 'tora', 'usagi']
    array2 = ['serval', 'araisan', 'vulpes_zerda', 'silverfox']
    array3 = ['one', 'two', 'three', 'four']
    assert {1: 'nezumi', 2: 'ushi', 3: 'tora',
            4: 'usagi'} == helper_def.numbering_dictionary(array1)
    assert {1: 'serval', 2: 'araisan', 3: 'vulpes_zerda',
            4: 'silverfox'} == helper_def.numbering_dictionary(array2)
    assert {2: 'one', 3: 'two', 4: 'three',
            5: 'four'} == helper_def.numbering_dictionary(array3, start_key=2)