LoginSignup
1
1

More than 1 year has passed since last update.

Google App EngineにPythonの開発環境を作成する。【Django版scikit learnアプリ掲載】

Last updated at Posted at 2019-08-16

クラウドの開発・公開はAzureのWebappsを、無償である事に感謝しつつ、常時接続ではない事やメモリ等の制限(1G)などからレスポンスは悪く、デバッグにはストレスが募っていました。そこでお盆休みを利用してGoogle App Engineにトライしました。
※本番デプロイができなかったのでDemoはありません。Azure版はこちら

構築手順

更新が早い為か、公式サイトでも自分でgoogleコマンド等いくつもインストールしなければならないように書かれているものもあったりしますが、以下の手順で簡単に設定できました。
1.Googleの検索サイトの右上のログインからGOOGLEアカウントにログイン。
2.Google Cloud Platformにアクセス。
gcp.jpg
3.無料トライアルをクリックし、同意事項に同意して進む。
4.クレジット情報などお客様情報を入力すればようこそが表示。
5.シェルを選択。
shell.jpg

ここまでで以下の環境ができあがります。

1.My First Projectというプロジェクトが初期生成されます。python2.7、openjdk1.8.0_222、node10.14.2(他にGOも)がライブラリのパスが切ってあり動作します。

2.pythonのライブラリを確認すると全部で93フォルダ、主なものとして以下がインストールされていました。

  google-apiコマンド各種
  google-cloudコマンド各種
  bigquery
  Keras-Applications
  numpy
  tensorflow
Flask,Django,sklearn,matlib,pandas,Pillowはありません。他のライブラリを追加する為には仮想環境を作る必要があるようです。仮想環境にしないままpip installしても追加されませんでした。これらのライブラリは他のAIプラットフォームなどを動かす際に使うのでしょう。

3.APIも初期状態で下記のようになっています。
api.jpg

Python開発環境の構築

1.コンソール画面の右上部の>_マークをクリックして、コンソールを立ち上げ、コンソール右の鉛筆マークをクリックするとエディタが立ち上がります。xxx_folderのところは、Linuxのホームディレクトリ「/home/ユーザー名」になります。
screen.jpg

2.仮想環境を作成します。コンソール画面から「virtualenv <任意のフォルダ名>」。バージョン無指定だと、最新3.7がインストールされます(2019/08現在)。Googleクラウドのコマンドは2.7でしか動きませんので、利用する場合は「--python=/usr/bin/python2.7」を付けてバージョン指定します。仮想環境を作った段階では、ライブラリはpip・setuptools・wheelだけが設定されます。

3.「cd <任意のフォルダ名>」で変更して、「source bin/activate」で仮想環境を起動します。(終了は「deactivate」。)

4.必要なライブラリをインストールします。(今回のサンプルを動かすには以下の通り。)

インストール
pip install Django
pip install sklearn
pandas
matplotlib
numpy
dtreeplt
pip install Pillow

5.ソースを配置します。

6.「python manage.py runserver 8080 」で起動します。

7.コンソール右上の👁マークをクリックしてプレビューします。

※デバッグ後に本番デプロイしようとしましたが、「too many files」エラーでアップできませんでした。

ソース

scikit learnから解釈過程を表示するWebアプリを作ってみたのDjango版のソースを掲載します。サンプルを動かすところのファイルのみです。管理画面も省略してます。

1.フォルダ構造


wwwrootDJ
├─common.py   ライブラリの読み込み等の共通ルーチン
├─manage.py   (自動生成ソース略)
├─Data
│  └─_dultCensusIncome_model.sav  nootbookで訓練したsavファイル
│ 
├─mysite
│  ├─settings.py
│  ├─urls.py
│  ├─wsgi.py    (自動生成ソース略)
│  └─init.py    (自動生成ソース略)
├─ml
│  ├─Income.py  メインのプログラム
│  └─static
│     └─result dtreepltの画像が保管されるフォルダ
│
└─templates
   ├─base.html  共通のhtml
   └─ml
     └─Income.html

2.Income.html

Income.html
<!--*****************************************************************
*  azureMLサンプル:米国国勢調査提供の収入のサンプルの訓練結果にrestで接続
*  
*  2019/03/24
*****************************************************************-->
{% extends "base.html" %}
{% block body %}

<form name='sendform' action="" style="display: inline" method="post">
<!-- form name='sendform' action="/Income/" style="display: inline" method="post"-->
<div class="card" >
<div class="card-header" style="height:50px; font-size:1.5rem; ">
    <b>scikit-learn  webアプリサンプル</b>
</div>
<div class="card-body">
    <div class="row">
        <div class="col-md-2 mb-1">年齢</div>      <!--col-md-1:mdはPCのMIDDLEサイズ。mb-1はマージン(空白)をボトム(下)に設定-->
        <div class="col-md-2 mb-1" >
            <select class="form-control input-lg mb-1" id="age">
                <option>20</option>
                <option>25</option>
                <option>30</option>
                <option>35</option>
                <option>40</option>
                <option>45</option>             
                <option>50</option>
                <option>55</option>             
                <option>60</option>
                <option>65</option>             
                <option>70</option>
                <option>75</option>             
                <option>80</option>
            </select>
        </div>
        <div class="col-md-2 mb-1">ワーククラス</div>
        <div class="col-md-2 mb-1" >
            <select class="form-control input-lg mb-1" id="workclass">
                <option value="Federal-gov">Federal-gov</option>
                <option value="Local-gov">Local-gov</option>
                <option value="Never-worked">Never-worked</option>
                <option value="Private">Private</option>
                <option value="Self-emp-inc">Self-emp-inc</option>
                <option value="Self-emp-not-inc">Self-emp-not-inc</option>
                <option value="State-gov">State-gov</option>
                <option value="Without-pay">Without-pay</option>
                <option value="Without-pay">Without-pay</option>
            </select>
        </div>

        <div class="col-md-1 mb-1">学校</div>
        <div class="col-md-3 mb-1" >
            <select class="form-control input-lg mb-1" id="education-num">
                <option value="1">Preschool</option>
                <option value="2">1st-4th</option>
                <option value="3">5th-6th</option>
                <option value="4">7th-8th</option>
                <option value="5">9th</option>
                <option value="6">10th</option>
                <option value="7">11th</option>
                <option value="8">12th</option>
                <option value="9">HS-grad</option>
                <option value="10">Some-college</option>
                <option value="11">Assoc-voc</option>
                <option value="12">Assoc-acdm</option>
                <option value="13">Bachelors</option>
                <option value="14">Masters</option>
                <option value="15">Prof-school</option>
                <option value="16">Doctorate</option>
            </select>
        </div>

    </div>

    <div class="row">

        <div class="col-md-1 mb-1">職業</div>
        <div class="col-md-3 mb-1" >
            <select class="form-control input-lg mb-1" id="occupation">
                <option value="Adm-clerical">Adm-clerical</option>
                <option value="Armed-Forces">Armed-Forces</option>
                <option value="Craft-repair">Craft-repair</option>
                <option value="Exec-managerial">Exec-managerial</option>
                <option value="Farming-fishing">Farming-fishing</option>
                <option value="Handlers-cleaners">Handlers-cleaners</option>
                <option value="Machine-op-inspct">Machine-op-inspct</option>
                <option value="Other-service">Other-service</option>
                <option value="Priv-house-serv">Priv-house-serv</option>
                <option value="Prof-specialty">Prof-specialty</option>
                <option value="Protective-serv">Protective-serv</option>
                <option value="Sales">Sales</option>
                <option value="Tech-support">Tech-support</option>
                <option value="Transport-moving">Transport-moving</option>
            </select>
        </div>

        <div class="col-md-2 mb-1">人種</div>
        <div class="col-md-2 mb-1" >
            <select class="form-control input-lg mb-1" id="race">
                <option value="Amer-Indian-Eskimo">Amer-Indian-Eskimo</option>
                <option value="Asian-Pac-Islander">Asian-Pac-Islander</option>
                <option value="Black">Black</option>
                <option value="Other">Other</option>
                <option value="White">White</option>
            </select>
        </div>

        <div class="col-md-2 mb-1">性別</div>
        <div class="col-md-2 mb-1" >
            <select class="form-control input-lg mb-1" id="sex">
                <option value="Male">Male</option>
                <option value="Female">Female</option>
            </select>
        </div>  

    </div>

    <div class="row">

        <div class="col-md-2 mb-1">キャピタルゲイン</div>
        <div class="col-md-2 mb-1" >
            <input  type="text" class="form-control input-lg" id="capital-gain" value=0 >
        </div>

        <div class="col-md-2 mb-1">キャピタルロス</div>
        <div class="col-md-2 mb-1" >
            <input  type="text" class="form-control input-lg" id="capital-loss" value=0 >
        </div>

        <div class="col-md-2 mb-1">労働時間</div>
        <div class="col-md-2 mb-1" >
            <input  type="text" class="form-control input-lg" id="hours-per-week" value=40 >
        </div>

    </div>

    <div class="row">

        <div class="col-md-1 mb-1">国</div>
        <div class="col-md-3 mb-1" >
            <select class="form-control input-lg mb-1" id="native-country">
                <option value="Cambodia">Cambodia</option>
                <option value="Canada">Canada</option>
                <option value="China">China</option>
                <option value="Columbia">Columbia</option>
                <option value="Cuba">Cuba</option>
                <option value="Dominican-Republic">Dominican-Republic</option>
                <option value="Ecuador">Ecuador</option>
                <option value="El-Salvador">El-Salvador</option>
                <option value="England">England</option>
                <option value="France">France</option>
                <option value="Germany">Germany</option>
                <option value="Japan">Japan</option>
                <option value="Laos">Laos</option>
                <option value="Mexico">Mexico</option>
                <option value="United-States">United-States</option>
                <option value="Vietnam">Vietnam</option>
                <option value="Yugoslavia">Yugoslavia</option>
            </select>
        </div>

        <div class="col-md-2 mb-1">アルゴリズム</div>
        <div class="col-md-4 mb-1" >
            <select class="form-control input-lg mb-1" id="alg">
                <option value="1">DecisionTree_model</option>
                <option value="2">RandomForest_model</option>
            </select>
        </div>  

    </div>

    <div class="row">
        <div class="col-md-2 mb-1" >
            <button class="btn btn-default" id="button1" style="width: 100px; padding: 5px;">Ajax Call</button>
        </div>
        <div class="col-md-3 mb-1" >
            <button class="btn btn-default" id="button2" style="width: 100px; padding: 5px;">Reload</button>
        </div>      
    </div>
    <div class="col-md-5 mb-1" >
            <h2>status→{{ status }}</h2>
            <textarea rows="40" cols="320">{{text}}</textarea><br>
    </div>


</div>  <!--class="card-body"の終端-->
</div>  <!--class="card"の終端-->

<div class="modal-body" style="width: 100%; overflow-x: auto;">
    <!-- モーダルは bodyからスクロールを削除して、代わりにモーダルコンテンツをスクロールする。-->
    {% load static %} <!-- Instruct Django to load static files  -->
    <img src="{% static file_name %}" width='2400' hight='2400' >
</div>

<input type="hidden" name="json_data" value="">
{% csrf_token %}

</form>


<script type="text/javascript">
//*************************************************
//*  form送信 
//*************************************************
$("#button2").click(function() {

    document.forms.sendform.json_data.value = JSON.stringify({
    "age":$("#age").val(),
    "workclass":$("#workclass").val(),
    "education-num":$("#education-num").val(),
    "occupation":$("#occupation").val(),
    "race":$("#race").val(),
    "sex":$("#sex").val(),
    "capital-gain":$("#capital-gain").val(),
    "capital-loss":$("#capital-loss").val(),
    "hours-per-week":$("#hours-per-week").val(),
    "native-country":$("#native-country").val(),
    "alg":$("#alg").val()
    });

    document.forms.submit();


});

</script>

{% endblock %}

3.Income.py

Income.py

 # -*- coding: utf-8 -*-
from common import *

 # Adult Census Income Binary Classification

 # 読み込むモデルをグローバル宣言
fn_RandomForest = './Data/AdultCensusIncome_model.sav'       

 #  incomeデータの前処理
def IncomeConvert(df):
    try:
        df['workclass'] = df['workclass'].map( {
        '?': 99,
        'Federal-gov':0,
        'Local-gov':1,
        'Never-worked':2,
        'Private':3,
        'Self-emp-inc':4,
        'Self-emp-not-inc':5,
        'State-gov':6,
        'Without-pay':7,
        } ).astype(int)
    except:
        log('erro1')

    try:
        df['occupation'] = df['occupation'].map( {
        '?': 99,
        'Adm-clerical':0,
        'Armed-Forces':1,
        'Craft-repair':2,
        'Exec-managerial':3,
        'Farming-fishing':4,
        'Handlers-cleaners':5,
        'Machine-op-inspct':6,
        'Other-service':7,
        'Priv-house-serv':8,
        'Prof-specialty':9,
        'Protective-serv':10,
        'Sales':11,
        'Tech-support':12,
        'Transport-moving':13,      
        } ).astype(int)
    except:
        log('erro2')  

    try:
        df['race'] = df['race'].map( {
        'White':0,
        'Amer-Indian-Eskimo':1,
        'Asian-Pac-Islander':2,
        'Black':4,
        'Other':5,
        } ).astype(int)
    except:
        log('erro3')  

    try:
        df['sex'] = df['sex'].map( {
            'Male': 0,
            'Female': 1,
            'Other': 2
        } ).astype(int)
    except:
        log('erro4')  

    try:
        df['native-country'] = df['native-country'].map( {
            '?': 99,
            'Cambodia':0,
            'Canada':1,
            'China':2,
            'Columbia':3,
            'Cuba':4,
            'Dominican-Republic':5,
            'Ecuador':6,
            'El-Salvador':7,
            'England':8,
            'France':9,
            'Germany':10,
            'Greece':11,
            'Guatemala':12,
            'Haiti':13,
            'Holand-Netherlands':14,
            'Honduras':15,
            'Hong':16,
            'Hungary':17,
            'India':18,
            'Iran':19,
            'Ireland':20,
            'Italy':21,
            'Jamaica':22,
            'Japan':23,
            'Laos':24,
            'Mexico':25,
            'Nicaragua':26,
            'Outlying-US(Guam-USVI-etc)':27,
            'Peru':28,
            'Philippines':29,
            'Poland':30,
            'Portugal':31,
            'Puerto-Rico':32,
            'Scotland':33,
            'South':34,
            'Taiwan':35,
            'Thailand':36,
            'Trinadad&Tobago':37,
            'United-States':38,
            'Vietnam':39,
            'Yugoslavia':40,
        } ).astype(int)
    except:
        log('erro5')

    try:
        df['income'] = df['income'].map( {
            '<=50K': 0,
            '>50K': 1,
        } ).astype(int)
    except:
        print('erro6') 

    return df

#**************************************
# TREEの各情報取得 https://scikit-learn.org/stable/auto_examples/tree/plot_unveil_tree_structure.html
#**************************************
def decisionTreeStructure(model,train_x,feature_names,target_names):

    text = ""
    text = text + '【InputData】\n' + str(train_x.loc[0]) + '\n'

    #入力の予測
    result = model.predict(train_x)
    text = text + "【Result】 = "  + target_names[result[0]] + "\n"

    #入力の到達するIDを求める
    leave_id = model.apply(train_x)
    leave_id = leave_id[0]                            #到達するID
    text = text + "【Leave_id】 = "  + str(leave_id) + "\n"

    #入力の到達する過程がスパース行列で得られる
    decision_path = model.decision_path(train_x)  
    node_indicator = str(decision_path) 
    text = text + "【Decision Process】 \n"  +  node_indicator + "\n"

    try:    
        #tree_のプロパティを得る
        n_nodes = model.tree_.node_count
        children_left = model.tree_.children_left   #左の子ノードID(子が無い場合は「-1」がセットされている) 
        children_right = model.tree_.children_right #右の子ノードID
        feature = model.tree_.feature               #特徴量のインデックス
        threshold = model.tree_.threshold           #閾値

        #各ノードの最多数クラス  https://own-search-and-study.xyz/2016/12/25/scikit-learn%e3%81%a7%e5%ad%a6%e7%bf%92%e3%81%97%e3%81%9f%e6%b1%ba%e5%ae%9a%e6%9c%a8%e6%a7%8b%e9%80%a0%e3%81%ae%e5%8f%96%e5%be%97%e6%96%b9%e6%b3%95%e3%81%be%e3%81%a8%e3%82%81/
        nodeClasses = np.argmax(model.tree_.value.T, axis=0)

        #################################################
        # 木構造をたどり各ノードの深さや葉であるかなどのプロパティを計算し木構造を表示する
        #################################################
        node_depth = np.zeros(shape=n_nodes, dtype=np.int64)
        is_leaves = np.zeros(shape=n_nodes, dtype=bool)
        stack = [(0, -1)]  # seed is the root node id and its parent depth

        #葉かどうか識別
        while len(stack) > 0:
            node_id, parent_depth = stack.pop()
            node_depth[node_id] = parent_depth + 1

            #子のノードが左右同じ(-1)だったら葉としてis_leavesにTrueをセット 
            if (children_left[node_id] != children_right[node_id]):
                stack.append((children_left[node_id], parent_depth + 1))
                stack.append((children_right[node_id], parent_depth + 1))
            else:
                is_leaves[node_id] = True

        text = text +  "【Binary tree structure has】= " + str(n_nodes) + " \n【Decision Tree Structure】\n"
        #表示
        for i in range(n_nodes):
            if is_leaves[i]:
                text = text +  (node_depth[i] * "\t") + "|-leaf  id=" + str(i) +  "  class= 【"+ target_names[nodeClasses[0][i]] + "】"
                if i == leave_id:
                    text = text +  "<---------------------------------[[[[*result*]]]"

                text = text  +"\n"
            else:
                text = text +  (node_depth[i] * "\t") + "|-node  id=" + str(i)
                text = text +  " if [" + feature_names[feature[i]] + "] <= [" + str(threshold[i])+  "] then node "  
                text = text +  str(children_left[i]) + " else to node " + str(children_right[i]) + "\n"

        #################################################
        #識別過程を表示する
        #################################################
        text = text +  "【Decision Process】\n"
        node_index = decision_path.indices[decision_path.indptr[0]:
                                       decision_path.indptr[1]]
        for node_id in node_index:
            if leave_id == node_id:
                continue

            w_train_x = str(train_x.loc[0][feature[node_id]])  #型がint・float・str混在して下記のif判定でエラーになるので強引に文字型にしている。
            w_threshold = str(threshold[node_id])

            #if (train_x.loc[0][feature[node_id]] <= threshold[node_id]):
            if (w_train_x <= w_threshold):
                threshold_sign = "<="
                next_node = str(children_left[node_id])
            else:
                threshold_sign = ">"
                next_node = str(children_right[node_id])

            text = text + "decision id= " + str(node_id) + "【"
            text = text + str(feature_names[feature[node_id]]) + "】"
            text = text + w_train_x + " "
            text = text + threshold_sign + " "
            text = text + w_threshold
            text = text + " goto " + next_node +  "  class= 【"+ target_names[nodeClasses[0][i]] +"】\n"
        return text
    except:
        return "!!decisionTreeStructureError!!"

#**************************************
# Adult Census Income Binary Classification dataset
#**************************************
#html表示

def Income(request):
    result  = {}
    try:
        dict_request = json.loads(request.POST['json_data'])
        df = pd.io.json.json_normalize(dict_request)   
        df = df.drop(['alg'],axis=1)  
        train_x = IncomeConvert(df)            
    except:
        result['status']  = 'No Input(Input Error)'
        return render(request, 'ml/Income.html',result)

    # 保存したモデルをロードする
    try:
        if dict_request['alg'] == "1":
            filename = fn_DecisionTree
        else:
            filename = fn_RandomForest
        model = pickle.load(open(filename, mode='rb'))
    except:
        result['status']  = 'model Error'
        return render(request, 'ml/Income.html',result)

    #incomeの特徴名・ターゲット名
    #feature_names=['age','workclass','education-num',
    #               'occupation','race','sex','capital-gain',
    #               'capital-loss','hours-per-week','native-country']

    feature_names = df.columns.values #df形式でも大丈夫
    target_names=['<=50k', '>50k']

    #Understanding the decision tree structure
    text = decisionTreeStructure(model,train_x,feature_names,target_names)

    # dtreeplt視覚化
    save_dir = 'ml/static/result/'
    save_file = 'Income-treeML.png'
    static_dir  = 'result/'

    try:
        dtree = dtreeplt(
            model=model,
            feature_names=feature_names ,
            target_names=target_names,
        )
        fig = dtree.view()
        fig.savefig( save_dir + save_file)

    except:
        result['status']  = 'dtreeplt Error'
        result['text']  = text
        result['file_name']  = static_dir + save_file
        return render(request, 'ml/Income.html',result)

    result['status']  = 'Ok'
    result['text']  = text
    result['file_name']  = static_dir + save_file
    return render(request, 'ml/Income.html',result)

 #  ajax送信スクリプト
def IncomeCalc(request):
    try:
        dict_request = json.loads(request.body) #jQueryのajaxデータはrequest.bodyに入っている。https://e-tec-memo.herokuapp.com/article/12/
        df = pd.io.json.json_normalize(dict_request)

        if dict_request['alg'] == "1":
            filename = fn_DecisionTree
        else:
            filename = fn_RandomForest   

        df = df.drop(['alg'],axis=1)           
        train_x = IncomeConvert(df)
    except:
        result = "ajaxSendError"  
        return HttpResponse(result)


    # 保存したモデルをロードする
    try:
        model = pickle.load(open(filename, mode='rb'))
        result = model.predict(train_x)

        if result[0] == 0:
            result = '<=50k' 
        else:
            result = '>50k' 
        leave_id = model.apply(train_x)
        node_indicator = model.decision_path(train_x)
        result = '\n predict = ' + result + '\n  leave_id = ' + str(leave_id[0])+ '\n  node_indicator = \n' + str(node_indicator[0])

    except:
        result = 'calc error'
        return HttpResponse(result)

    return HttpResponse(result)

 #  学習とモデルの保存
def IncomeClassLearn(request):
    result  = {}
    try:     
        df = pd.read_csv('./Data/Data_Adult.csv')
        df = IncomeConvert(df)
        df = df.drop(['fnlwgt','education','marital-status','relationship'],axis=1)

        train_x = df.drop('income', axis=1)
        train_y = df.income

        (train_x, test_x ,train_y, test_y) = train_test_split(train_x, train_y, test_size = 0.1, random_state = 666)

        # 決定木
        from sklearn.tree import DecisionTreeClassifier
        model = DecisionTreeClassifier(max_depth=5)   # ←depthを指定
        model = model.fit(train_x, train_y)
        pred = model.predict(test_x)
        log('pred=' + str(pred))

        # モデルの保存
        filename = './Data/AdultCensusIncome_model(WEBAPPS-depth-5).sav'
        pickle.dump(model, open(filename, 'wb'))
    except:
        result['status']  = 'Error'        
        result['text']  = 'IncomeClassLearn Error'
        return render(request, 'msg.html',result)

    result['status']  = 'OK'        
    result['text']  = 'IncomeClassLearn Model create & Save[AdultCensusIncome_model(WEBAPPS-depth-5).sav]OK'
    return render(request, 'msg.html',result)

def IncomeClass(request):
    result  = {}
    try:   
        df = pd.read_csv('./Data/Input_Adult.csv')
        df = IncomeConvert(df)   
        df = df.drop(['fnlwgt','education','marital-status','relationship'],axis=1)
        train_x = df.drop('income', axis=1)
        text = 'Input-------- \n' + str(train_x) + '\n' 
    except:
        result['status']  = 'Error'        
        result['text']  = 'CSV読込・変換エラー'
        return render(request, 'msg.html',result)

    # 保存したモデル(グローバル宣言してある)をロードする
    filename = fn_DecisionTree
    try:
        model = pickle.load(open(filename, 'rb'))
        pred_result = model.predict(train_x)
        text = text + 'predict-------- \n' + str(pred_result) + '\n' 
    except:
        result['status']  = 'Error'        
        result['text']  = 'ロード・予測エラー'
        return render(request, 'msg.html',result)

    result['status']  = 'OK'        
    result['text']  = text
    return render(request, 'msg.html',result)

4.その他

other
【settings.py】
from common import *

import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = 'am$+th5ttev9oa$77w9428jl*2bd-&fpvcy(dkigg99beg(kjh'
DEBUG = True

# 許可するサーバは自分のサーバ名に修正
ALLOWED_HOSTS = ['127.0.0.1']
#ALLOWED_HOSTS = ['dja-vin.azurewebsites.net']
#ALLOWED_HOSTS = ['dja-mako.azurewebsites.net']

# 作成したアプリを追加
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
    'ml',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
#    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

# ルートとして使用するコントローラ。デフォルトで生成されたものをそのまま利用。
ROOT_URLCONF = 'mysite.urls'

# CSSなどの静的ファイルの置き場所の設定
STATIC_ROOT = os.path.join(BASE_DIR, "static") #collectstatic コマンドを使って集めたいパス
STATIC_URL = '/static/'


# テンプレートの置き場所の設定
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'django.template.context_processors.static',
            ],
        },
    },
]

WSGI_APPLICATION = 'mysite.wsgi.application'

# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'
USE_I18N = True
USE_L10N = True
USE_TZ = True
DEBUG = True

-----------------------------------------------------------
【urls.py】
from django.contrib import admin
from django.urls import path
from django.conf.urls.static import static
from ml     import Income

urlpatterns = [
    path('',   Income.Income),
]

web.config、base.html、common.pyはこちらをご覧ください。

GAEとAzure Webapps(無償F1)の比較

比較内容 GAE Azure Webapps コメント
環境 LinuxDebianベース。ディスク5G。メモリ2G。xeon2.2G1コア。本番環境のWebサーバはおそらくnginx。 WindowsとLinux。ディスク1G。メモリ1G。CPUは不明、CPUは共有されている。WebサーバはWindows環境ではIIS。 GAEの方が広くて速い
開発環境と本番環境 開発環境と本番環境にわかれている。本番移行はコンテナイメージを作成し、イメージをAppEngineフレキシブル環境にデプロイするとの事。開発環境で動作したものをアップできるなど確実な運用が可能。 区別は無くWebAppsをふたつ立てるなどの工夫が必要
デバッグのしやすさ Pythonのエラーはそのまま吐き出され、print文もコンソールに吐き出すのでデバッグはローカルPCと同等に可能。 IISからPythonプログラムが起動されるので、エラーもprint文も戻らない。デバッグはやや難しい。
FTPによるソースの操作 不可。 可能。これによりWebappsのテストサーバのソースをフォルダごと本番サーバに移行できる等、便利。

本番デプロイができなかった為、現時点のGAEは、Webappsでうまくプログラムが動かない際の、ローカルPCのPython検証環境に代わるものという位置づけとなりました。
処理はローカルPC・Azureより早いので引き続き、本番環境へのデプロイは試していきたいと思います。

1
1
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
1
1