본문 바로가기
  • 문과생의 백엔드 개발자 성장기
|Playdata_study/JavaScript

210914_Vue.js2 (Axios, flask - CORS)

by 케리's 2021. 9. 15.

 

• flask - Axios 연결 (CORS)

 

 

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여,

 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제 

 

 

 

 

anaconda3 명령 프롬프트에서 설치

 

pip install flask_cors

 

 

 

from flask import Flask, request, make_response, jsonify
from flask_cors import CORS

app = Flask(__name__)
CORS(app)


@app.route("/emps", methods=['GET'])
def get_emps():

    return jsonify(
        [
            {"id": 1, "name": "James", "mailid": "sky98", "start_date": "1990-03-03", "manager_id": 0,
                "title": "사장", "dept_id": 118, "salary": 5000.0, "commission_pct": 0.0},
            {"id": 2, "name": "Peter", "mailid": "2xxsig", "start_date": "1990-03-08", "manager_id": 1,
                "title": "기획부장", "dept_id": 110, "salary": 3000.0, "commission_pct": 0.0},
            {"id": 3, "name": "Scott", "mailid": "ch21175", "start_date": "1991-06-17", "manager_id": 1,
                "title": "영업부장", "dept_id": 102, "salary": 3500.0, "commission_pct": 0.0},
            {"id": 4, "name": "Tiger", "mailid": "mquickto", "start_date": "1990-04-07",
                "manager_id": 1, "title": "총무부장", "dept_id": 101, "salary": 3000.0, "commission_pct": 0.0},
            {"id": 5, "name": "Tomson", "mailid": "aropebur", "start_date": "1990-03-04",
                "manager_id": 1, "title": "인사부장", "dept_id": 118, "salary": 3200.0, "commission_pct": 0.0},
            {"id": 6, "name": "Brion", "mailid": "murguhar", "start_date": "1991-01-18",
                "manager_id": 2, "title": "과장", "dept_id": 110, "salary": 2400.0, "commission_pct": 0.0},
            {"id": 7, "name": "Somin", "mailid": "enchu", "start_date": "1990-05-14",
                "manager_id": 2, "title": "과장", "dept_id": 111, "salary": 2400.0, "commission_pct": 0.0}
        ]
    )


if __name__ == '__main__':
    app.run(host="0.0.0.0", port="7777")

 

 

 

<!-- emp.html -->

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <link rel="stylesheet" type="text/css" href="../static/table.css" />
    <title>ENCORE HRM</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id='app'>
        <div class='headtitle'>
            <h2>ENCORE HRM LIST</h2>
        </div>
        <div class='search_box'>
            <a href='./index04.html'>사원추가</a> |
            <a href='./index03.html'>이름으로 찾기</a> |
            <a href='./index02.html'>아이디로 찾기</a> |
            <a href='./emp.html'>모든사원 보기</a>
        </div>

        <!-- <div class="search_box" align='center'>
            이름으로 사원 찾기 : <input type="text" name="name" v-model='cname'>
        </div> -->

        <table class='list_table'>
            <col width="10%">
            <col width="20%">
            <col width="20%">
            <col width="20%">
            <col width="10%">
            <thead>
                <row>
                    <th>사원 아이디</th>
                    <th>사원명</th>
                    <th>부서</th>
                    <th>직책</th>
                    <th>연봉</th>
                </row>
            </thead>
            <tbody>
                <tr v-for="emp in info">
                    <td v-html="emp.id"></td>
                    <td v-html="emp.name"></td>
                    <td v-html="emp.dept_id"></td>
                    <td v-html="emp.title"></td>
                    <td>{{emp.salary | salarydecimal}}</td>
                </tr>
            </tbody>
        </table>
    </div>
    <script>
        new Vue({
            el: "#app",
            data() {
                return {
                    info: [],
                    loading: true,
                    errored: false
                }
            },
            filters: {
                salarydecimal(value) {
                    return value.toFixed(2);
                }
            },
            mounted() {
                axios
                    .get('http://127.0.0.1:7777/emps') //비동기요청
                    .then(response => (this.info = response.data))
                    .catch(error => {
                        console.log(error);
                        this.errored = true
                    })
                    .finally(() => this.loading = false)
            }
        })
    </script>
</body>

</html>

 

 

/*table.css*/

@CHARSET "UTF-8";

/* input elements */

input {
    font-size: 12px;
}

textarea {
    font-size: 12px;
}

input.txt {
    padding-top: 3px;
    height: 18px;
}

select {
    font-size: 12px;
    height: 20px;
}

.headtitle {
    margin: auto;
    padding: 0px;
    text-align: center;
    color: #f3796f;
    font-family: serif;
}

.headtitle>h2 {
    margin: auto;
    padding: 0px;
    text-align: center;
    color: #f3796f;
    font-family: serif;
}

.list_table {
    width: 98%;
    border-bottom: 1px solid #EFEFEF;
    border-right: 1px solid #EFEFEF;
    border-collapse: collapse;
    margin-left: auto;
    margin-right: auto;
    clear: both;
}

.list_table2 {
    width: 98%;
    border-bottom: 1px solid #EFEFEF;
    border-right: 1px solid #EFEFEF;
    border-collapse: collapse;
    margin-left: auto;
    margin-right: auto;
    clear: both;
    vertical-align: middle;
    text-align: center
}

.list_table td,
.list_table th {
    text-align: center;
    border-top: 1px solid #EFEFEF;
    border-left: 1px solid #EFEFEF;
    padding: 0.3em;
}

.list_table th {
    background-color: #ff7373;
    color: #FFFFFF;
    line-height: 1.7em;
    font-weight: normal;
}

.list_table tr td {
    padding-top: 0.5em;
    padding-bottom: 0.5em;
}

.list_table td.title {
    padding-left: 0.5em;
    text-align: left;
}

.list_table td.title a:hover {
    text-decoration: underline;
}

.list_table th,
.list_table td {
    vertical-align: middle;
}

.list_table select {
    height: 19px;
    border: #CCCCCC solid 1px;
    vertical-align: middle;
    line-height: 1.8em;
    padding-left: 0px;
}

.list_table select option {
    margin-right: 10px;
}

.list_table .selected_row {
    border: solid 0px #EFEFEF;
}

.list_table .sele {
    padding: 0;
    margin: 0;
}

.content_table {
    width: 98%;
    border-bottom: 1px solid #EFEFEF;
    border-right: 1px solid #EFEFEF;
    border-collapse: collapse;
    margin-left: auto;
    margin-right: auto;
    clear: both;
}

.content_table td,
.content_table th {
    text-align: center;
    border-top: 1px solid #EFEFEF;
    border-left: 1px solid #EFEFEF;
    padding: 0.3em;
}

.content_table th {
    background-color: #ff7373;
    color: #FFFFFF;
    line-height: 1.7em;
    font-weight: normal;
}

.content_table td {
    padding-left: 5px;
    text-align: left;
    line-height: 1.7em;
}

.content_table td.contents {
    width: 100%;
    height: 400px;
    overflow: auto;
}

.content_table th,
.content_table td {
    vertical-align: middle;
}

.content_table select {
    height: 19px;
    border: #CCCCCC solid 1px;
    vertical-align: middle;
    line-height: 1.8em;
    padding-left: 0px;
}

.content_table select option {
    margin-right: 10px;
}

#files_wrap {
    width: 99.5%;
    margin-left: auto;
    margin-right: auto;
}

#files_wrap #_file_list_item {
    list-style-type: disc;
}

.box_border {
    width: 98%;
    border-width: 1px;
    border-style: solid;
    border-color: #EFEFEF;
    background-color: #FFFFFF;
    text-align: right;
    padding: 2px;
    margin-top: 5px;
    margin-bottom: 10px;
    margin-left: auto;
    margin-right: auto;
}

.search_box {
    width: 98%;
    border-width: 1px;
    border-style: solid;
    border-color: #EFEFEF;
    background-color: #FFFFFF;
    padding: 2px;
    padding-top: 7px;
    padding-bottom: 7px;
    margin-top: 5px;
    margin-bottom: 10px;
    margin-left: auto;
    margin-right: auto;
    margin-top: 5px;
    margin-bottom: 10px;
    text-align: center;
    line-height: 2.1em;
}

.items_title {
    text-align: left;
    margin-left: auto;
    margin-right: auto;
    padding-left: 28px;
    padding-top: 3px;
    background-image: url("../image/shape_square.png");
    background-repeat: no-repeat;
    background-position: 10px 0px;
    margin-top: 25px;
    margin-bottom: 1px;
    font-size: 12px;
    font-weight: bold;
}

.items_description {
    width: 98%;
    border-width: 1px;
    border-style: solid;
    border-color: #EFEFEF;
    background-color: #FFFFFF;
    text-align: left;
    line-height: 2.5;
    padding: 2px;
    margin: 0px;
}

.checkbox_lable {
    vertical-align: 2px;
    margin-left: 5px;
    margin-right: 5px;
    _vertical-align: 4px;
    _margin-left: 0px;
    _margin-right: 2px;
    /* IE6 Hack */
}

.go_view {
    cursor: pointer;
    text-align: left;
    padding-left: 3px;
}

.calendar_icon {
    cursor: pointer;
    border: 0;
    width: 22px;
    vertical-align: -6px;
    *vertical-align: -6px;
}

.tored {
    color: #aabbcc;
    background-color: #ddbbaa
}

 

 

 

1. emp.html 실행 

 

 

 

 

2. flask_emp.py  실행 → 서버 올린다 (서버가동, Ready On)

 

 

 

 

 

 

3. http://127.0.0.1:7777/emps 로 url 수정 

 

 

 

 

 

 

 

4. emp.html 새로고침 → 비동기 확인

 

 

 

 

 

4. 이클립스(port : 9999) 열어서  emp.html 파일 실행해도 같은 결과를 얻을 수 있다.

    (flask_emp.py / port : 7777) 네트워크 LAN만 연결되어 있으면 가능

 

 

 

 

 

• bootstrap

 

 

 1. 예제

 

# flask_server_before.py

from flask import Flask, request, jsonify
from flask_cors import CORS

app = Flask(__name__)
CORS(app)


@app.route("/boot_before", methods=["GET"])
def boot_test():
    return jsonify(success=True)


if __name__ == "__main__":
    app.run(host="0.0.0.0", port="7777")

 

 

<!-- boot_before.html -->

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!--bootstrap > getstarted > introduction ::  CSS, JS cdr -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/bQdsTh/da6pkI1MST/rWKFNjaCP5gBSY4sEBT38Q/9RBh9AH40zEOg7Hlq2THRZ" crossorigin="anonymous"></script>
    <!--Axios, Vue 라이브러리 추가-->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div class="accordion" id="accordionExample">
        <div class="accordion-item">
            <h2 class="accordion-header" id="headingOne">
                <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
              Accordion Item #1
            </button>
            </h2>
            <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
                <div class="accordion-body">
                    <strong>This is the first item's accordion body.</strong> It is shown by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the
                    showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition
                    does limit overflow.
                </div>
            </div>
        </div>
        <div class="accordion-item">
            <h2 class="accordion-header" id="headingTwo">
                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
              Accordion Item #2
            </button>
            </h2>
            <div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
                <div class="accordion-body">
                    <strong>This is the second item's accordion body.</strong> It is hidden by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as
                    the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the
                    transition does limit overflow.
                </div>
            </div>
        </div>
        <div class="accordion-item">
            <h2 class="accordion-header" id="headingThree">
                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
              Accordion Item #3
            </button>
            </h2>
            <div id="collapseThree" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
                <div class="accordion-body">
                    <strong>This is the third item's accordion body.</strong> It is hidden by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the
                    showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition
                    does limit overflow.
                </div>
            </div>
        </div>
    </div>
    <P>
        <HR>
    </P>
    <!-- 아래 영역은 Vue 내용이 추가되는 영역-->
    <div id="app">
        <center>
            <!-- bootstrap > button > copy -->
            <button type="button" class="btn btn-primary" @click="axios_btn">GET</button>
            <button type="button" class="btn btn-secondary">POST</button>
            <button type="button" class="btn btn-success">PUT</button>
            <button type="button" class="btn btn-danger">DELETE</button>
        </center>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            methods: {
                axios_btn() {
                    axios
                        .get("http://127.0.0.1:7777/boot_before")
                        .then((response) => {
                            console.log(response.data) //success=True 리턴
                        })
                        .catch((error) => {
                            console.log(error)
                        })
                }
            }
        });
    </script>
</body>

</html>

 

 

 

 

 

 

 

 

 2. 예제

 

 

from flask import Flask, request, jsonify, make_response
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

# routiong


@app.route("/boot", methods=["GET", "POST", "PUT", "DELETE"])
def boot_test():
    if request.method == "GET":
        print("get, 요청입니다")
        info = request.args.get("email")
        print(info)
    if request.method == "POST":
        print("post, 요청입니다")
        data = request.get_json()  # post 일때는 객체이기 때문에 get_json
        print(data)  # 객체값이 나옴
        print(data['email'])  # 요소값이 나옴
    if request.method == "PUT":
        print('put, 요청입니다')
        data = request.get_json()
        print(data)
        print(data['email'])
    if request.method == "DELETE":
        print("delete, 요청입니다.")
        info = request.args.get("email")
        print(info)
    return make_response(jsonify({'status': True}), 200)


if __name__ == "__main__":
    app.run(host="127.0.0.1", port="7788")

 

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/bQdsTh/da6pkI1MST/rWKFNjaCP5gBSY4sEBT38Q/9RBh9AH40zEOg7Hlq2THRZ" crossorigin="anonymous"></script>
    <!--Axios ajax 추가-->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div class="accordion" id="accordionExample">
        <div class="accordion-item">
            <h2 class="accordion-header" id="headingOne">
                <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
              Accordion Item #1
            </button>
            </h2>
            <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
                <div class="accordion-body">
                    <strong>This is the first item's accordion body.</strong> It is shown by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the
                    showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition
                    does limit overflow.
                </div>
            </div>
        </div>
        <div class="accordion-item">
            <h2 class="accordion-header" id="headingTwo">
                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
              Accordion Item #2
            </button>
            </h2>
            <div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
                <div class="accordion-body">
                    <strong>This is the second item's accordion body.</strong> It is hidden by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as
                    the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the
                    transition does limit overflow.
                </div>
            </div>
        </div>
        <div class="accordion-item">
            <h2 class="accordion-header" id="headingThree">
                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
              Accordion Item #3
            </button>
            </h2>
            <div id="collapseThree" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
                <div class="accordion-body">
                    <strong>This is the third item's accordion body.</strong> It is hidden by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the
                    showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition
                    does limit overflow.
                </div>
            </div>
        </div>
    </div>
    <p>
        <HR>
    </p>
    <!-- 아래 영역은 Vue 내용이 추가되는 영역-->
    <div id="app">
        <center>
            <button type="button" class="btn btn-primary" @click="get_btn">GET</button>
            <button type="button" class="btn btn-secondary" @click="post_btn">POST</button>
            <button type="button" class="btn btn-success" @click="put_btn">PUT</button>
            <button type="button" class="btn btn-danger" @click="del_btn">DELETE</button>
        </center>
        <p>E-mail : <b>{{email}}</b></p>
    </div>

    <script>
        var app = new Vue({
            el: '#app',
            data: {
                email: "hyerihello@gmail.com"
            },
            methods: {
                get_btn() {
                    axios
                        .get("http://127.0.0.1:7788/boot?email=" + this.email)
                        .then((response) => {
                            console.log(response.data)
                        })
                        .catch((error) => {
                            console.log(error)
                        })
                },
                post_btn() {
                    axios
                        .post("http://127.0.0.1:7788/boot", {
                            email: this.email
                        })
                        .then((response) => {
                            console.log(response.data)
                        })
                        .catch((error) => {
                            console.log(error)
                        })
                },
                put_btn() {
                    axios
                        .put("http://127.0.0.1:7788/boot", {
                            email: this.email
                        })
                        .then((response) => {
                            console.log(response.data)
                        })
                        .catch((error) => {
                            console.log(error)
                        })
                },
                del_btn() {
                    axios
                        .delete("http://127.0.0.1:7788/boot?email=" + this.email)
                        .then((response) => {
                            console.log(response.data)
                        })
                        .catch((error) => {
                            console.log(error)
                        })
                }
            }
        });
    </script>
</body>

</html>

 

 

 

 

 

 


final

 

 

• sklearn.model  data(iris) 를 활용한  학습 및 flask와 axios 연결하기

 

 

import numpy as np
import pandas as pd
from pandas import DataFrame
from sklearn.model_selection import train_test_split

# 밑에서 weight값 저장하기 위한 모듈
# 파이썬에서 텍스트 이외의 자료형을 파일로 저장할때 필요한 모듈. dump() 사용된다.
import pickle
import seaborn as sns
from sklearn.svm import SVC

iris = sns.load_dataset('iris')
print(iris.head()) # 데이터 잘 가져오는지 확인

 

# 데이터 잘 가져오는지 확인 

Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

새로운 크로스 플랫폼 PowerShell 사용 https://aka.ms/pscore6

개인 및 시스템 프로필을 로드하는 데 1012ms가 걸렸습니다.
(base) PS C:\encore_hr\flask_workspace> conda activate base
(base) PS C:\encore_hr\flask_workspace> & C:/anaconda3/python.exe c:/encore_hr/flask_workspace/05_final_ai/iris_model.py
   sepal_length  sepal_width  petal_length  petal_width species        
0           5.1          3.5           1.4          0.2  setosa        
1           4.9          3.0           1.4          0.2  setosa        
2           4.7          3.2           1.3          0.2  setosa        
3           4.6          3.1           1.5          0.2  setosa        
4           5.0          3.6           1.4          0.2  setosa        
(base) PS C:\encore_hr\flask_workspace>

 

 

 

 

 

전체  iris_model.py 코드

 

# iris_model.py

import numpy as np
import pandas as pd
from pandas import DataFrame
from sklearn.model_selection import train_test_split

# 밑에서 weight값 저장하기 위한 모듈
# 파이썬에서 텍스트 이외의 자료형을 파일로 저장할때 필요한 모듈. dump() 사용된다.
import pickle
import seaborn as sns
from sklearn.svm import SVC

iris = sns.load_dataset('iris')
# print(iris.head())


X = iris[['sepal_length', 'sepal_width',
          'petal_length', 'petal_width']]  # 2차원 [[]] 사용
y = iris['species']  # target


# 모델 분류
x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

# 모델 생성
model = SVC()

# 모델 학습
model.fit(x_train, y_train)

# 이 부분을 해주면 된다.
# 모델의 학습결과 :: model = weight 값
pickle.dump(model, open('model.pkl', 'wb'))  # weight값이 저장됨, 확장자 pkl로 저장하기

 

파일 생성 확인

 

 

 

flask_server.py 코드 

 

#flask_server.py

from flask import Flask, render_template, request, jsonify
import numpy as np
import pickle

# model.pkl 파일을 받아온다. (읽기)
model = pickle.load(open('model.pkl', 'rb'))

# 생성된 flask 기능을 app에 할당
app = Flask(__name__)


# 웹 서비스 추가


@app.route("/")
def home():
    return render_template('index.html')

# index.html 에서 <form action="predict" method="post"> 이랑 맞춰준다.


@app.route("/predict", methods=['POST'])
def predict():
    # post로 넘어오는 값이  .method 부분 전부 다 받는다 :: request.form.values() 함수사용
    # 배열로 들어오기 때문에 [] 안에 넣어주고 그것을 for문 사용한다.
    # 들어오는 값이 float 형식이기 때문에 형변환 함수 float(x) 를 앞에 써준다.
    init_feature = [float(x) for x in request.form.values()]
    # 예측할 때 사용하기 위해 numpy 배열로 변경해준다.
    final_feature = [np.array(init_feature)]

    # 예측값 :: prediction , 모델예측 :: model.predict
    prediction = model.predict(final_feature)

    # 결과를 index.html로 보낸다.
    # ginger문법 ::  prediction_text='Prediction Class {}'.format(prediction)
    return render_template("index.html", prediction_text='Prediction Class {}'.format(prediction))


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

 

 

 

 index.html 코드

 

<!-- index.html-->

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href='https://fonts.googleapis.com/css?family=Pacifico' rel='stylesheet' type='text/css'>
    <link href='https://fonts.googleapis.com/css?family=Arimo' rel='stylesheet' type='text/css'>
    <link href='https://fonts.googleapis.com/css?family=Hind:300' rel='stylesheet' type='text/css'>
    <link href='https://fonts.googleapis.com/css?family=Open+Sans+Condensed:300' rel='stylesheet' type='text/css'>
    <link rel="stylesheet" href="../static/css/style.css">

</head>

<body>
    <div class="login">
        <h1>Predict IRIS Class</h1>
        <form action="predict" method="post">
            <!--required="required" 입력값이 요청되도록-->
            <input type="text" name="sepal_length" placeholder="Sepal_Length(cm)" required="required">
            <input type="text" name="sepal_width" placeholder="Sepal_Width(cm)" required="required">
            <input type="text" name="petal_length" placeholder="Petal_Length(cm)" required="required">
            <input type="text" name="petal_width" placeholder="Petal_Width(cm)" required="required">
            <!-- submit :: action 의 페이지영역을 가지고 올때 모든 form의 값을 가져옴 -->
            <button type="submit" class="btn btn-primary btn-block btn-large">Predict</button>
        </form>
        <br/>
        <br/> {{prediction_text}}
        <!-- 예측값이 이쪽으로 출력된다.-->
    </div>
</body>

</html>

 

 

 

style.css 코드

 

/* style.css */

@import url(https://fonts.googleapis.com/css?family=Open+Sans);
.btn { display: inline-block; *display: inline; *zoom: 1; padding: 4px 10px 4px; margin-bottom: 0; font-size: 13px; line-height: 18px; color: #333333; text-align: center;text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); vertical-align: middle; background-color: #f5f5f5; background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); background-image: linear-gradient(top, #ffffff, #e6e6e6); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr=#ffffff, endColorstr=#e6e6e6, GradientType=0); border-color: #e6e6e6 #e6e6e6 #e6e6e6; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); border: 1px solid #e6e6e6; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); cursor: pointer; *margin-left: .3em; }
.btn:hover, .btn:active, .btn.active, .btn.disabled, .btn[disabled] { background-color: #e6e6e6; }
.btn-large { padding: 9px 14px; font-size: 15px; line-height: normal; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; }
.btn:hover { color: #333333; text-decoration: none; background-color: #e6e6e6; background-position: 0 -15px; -webkit-transition: background-position 0.1s linear; -moz-transition: background-position 0.1s linear; -ms-transition: background-position 0.1s linear; -o-transition: background-position 0.1s linear; transition: background-position 0.1s linear; }
.btn-primary, .btn-primary:hover { text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); color: #ffffff; }
.btn-primary.active { color: rgba(255, 255, 255, 0.75); }
.btn-primary { background-color: #4a77d4; background-image: -moz-linear-gradient(top, #6eb6de, #4a77d4); background-image: -ms-linear-gradient(top, #6eb6de, #4a77d4); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#6eb6de), to(#4a77d4)); background-image: -webkit-linear-gradient(top, #6eb6de, #4a77d4); background-image: -o-linear-gradient(top, #6eb6de, #4a77d4); background-image: linear-gradient(top, #6eb6de, #4a77d4); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr=#6eb6de, endColorstr=#4a77d4, GradientType=0);  border: 1px solid #3762bc; text-shadow: 1px 1px 1px rgba(0,0,0,0.4); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.5); }
.btn-primary:hover, .btn-primary:active, .btn-primary.active, .btn-primary.disabled, .btn-primary[disabled] { filter: none; background-color: #4a77d4; }
.btn-block { width: 100%; display:block; }

* { -webkit-box-sizing:border-box; -moz-box-sizing:border-box; -ms-box-sizing:border-box; -o-box-sizing:border-box; box-sizing:border-box; }

html { width: 100%; height:100%; overflow:hidden; }

body { 
	width: 100%;
	height:100%;
	font-family: 'Open Sans', sans-serif;
	background: #092756;
	color: #fff;
	font-size: 18px;
	text-align:center;
	letter-spacing:1.2px;
	background: -moz-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%),-moz-linear-gradient(top,  rgba(57,173,219,.25) 0%, rgba(42,60,87,.4) 100%), -moz-linear-gradient(-45deg,  #670d10 0%, #092756 100%);
	background: -webkit-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -webkit-linear-gradient(top,  rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -webkit-linear-gradient(-45deg,  #670d10 0%,#092756 100%);
	background: -o-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -o-linear-gradient(top,  rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -o-linear-gradient(-45deg,  #670d10 0%,#092756 100%);
	background: -ms-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -ms-linear-gradient(top,  rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -ms-linear-gradient(-45deg,  #670d10 0%,#092756 100%);
	background: -webkit-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), linear-gradient(to bottom,  rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), linear-gradient(135deg,  #670d10 0%,#092756 100%);
	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3E1D6D', endColorstr='#092756',GradientType=1 );

}
.login { 
	position: absolute;
	top: 40%;
	left: 50%;
	margin: -150px 0 0 -150px;
	width:400px;
	height:400px;
}

.login h1 { color: #fff; text-shadow: 0 0 10px rgba(0,0,0,0.3); letter-spacing:1px; text-align:center; }

input { 
	width: 100%; 
	margin-bottom: 10px; 
	background: rgba(0,0,0,0.3);
	border: none;
	outline: none;
	padding: 10px;
	font-size: 13px;
	color: #fff;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.3);
	border: 1px solid rgba(0,0,0,0.3);
	border-radius: 4px;
	box-shadow: inset 0 -5px 45px rgba(100,100,100,0.2), 0 1px 1px rgba(255,255,255,0.2);
	-webkit-transition: box-shadow .5s ease;
	-moz-transition: box-shadow .5s ease;
	-o-transition: box-shadow .5s ease;
	-ms-transition: box-shadow .5s ease;
	transition: box-shadow .5s ease;
}
input:focus { box-shadow: inset 0 -5px 45px rgba(100,100,100,0.4), 0 1px 1px rgba(255,255,255,0.2); }

 

 

 

 

 

 

 

댓글