[スパルタコードクラブ]ネットワーク開発総合クラス-4週間(2)


2週目のタスクでのオーダー履歴の呼び出し

コード#コード#


app.py

from flask import Flask, render_template, jsonify, request

app = Flask(__name__)

from pymongo import MongoClient

client = MongoClient('localhost', 27017)
db = client.dbhomework


## HTML 화면 보여주기
@app.route('/')
def homework():
    return render_template('index.html')


# 주문하기(POST) API
@app.route('/order', methods=['POST'])
def save_order():
    name_receive = request.form['name_give']
    phone_receive = request.form['phone_give']
    address_receive = request.form['address_give']
    options_receive = request.form['options_give']

    doc = {
        'name':name_receive,
        'phone':phone_receive,
        'address':address_receive,
        'options':options_receive
    }

    db.thewitch.insert_one(doc)

    return jsonify({'result': 'success', 'msg': 'Thank you for your order!'})


# 주문 목록보기(Read) API
@app.route('/order', methods=['GET'])
def view_orders():
    orders = list(db.thewitch.find({}, {'_id': False}))
    return jsonify({'all_orders': orders})


if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)

index.html

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

<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
            integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
            crossorigin="anonymous"></script>

    <title>A24</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link
            href="https://fonts.googleapis.com/css2?family=Noto+Serif+KR:wght@500&family=Raleway:ital,wght@1,200;1,400;1,800&display=swap"
            rel="stylesheet">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Gothic+A1:wght@100&display=swap" rel="stylesheet">

    <style>
        * {
            font-family: 'Noto Serif KR', serif;
            font-family: 'Raleway', sans-serif;
        }

        .img {
            width: 40%;
            height: 500px;
            margin: 50px auto;
            background-image: url(https://cdn.shopify.com/s/files/1/0023/3789/8540/products/The-Witch-book-share_e38e1f2a-6718-4a53-8a7a-855736967a21.jpg?v=1618330188%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//cdn.shopify.com/s/files/1/0023/3789/8540/products/The-Witch-book-share_e38e1f2a-6718-4a53-8a7a-855736967a21.jpg?v=1618330188%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//cdn.shopify.com/s/files/1/0023/3789/8540/products/The-Witch-book-share_e38e1f2a-6718-4a53-8a7a-855736967a21.jpg?v=1618330188);
            background-position: center;
            background-size: cover;
            border: 1px double;
            box-shadow: 3px 3px 0px rgb(107, 106, 106);
        }

        .description {
            width: 40%;
            margin: auto auto 50px;
        }

        .title {
            font-size: 35px;
            font-weight: 800;
        }

        .price {
            font-size: 20px;
            font-weight: 400;
            margin: 0 0 0 5px;
        }

        .text {
            margin: 25px auto;
            color: rgb(140, 139, 139);
            font-weight: 200;
        }

        .order {
            width: 40%;
            margin: auto auto 100px auto;
        }

        .input-group-text {
            color: black;
            background-color: #fff;
            border: 1px dashed;
            border-radius: 0%;
            width: 100px;
            text-align: center;
            padding-left: 20px;
        }
        .input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),
        .input-group>.input-group-append:last-child>.input-group-text:not(:last-child),
        .input-group>.input-group-append:not(:last-child)>.btn,
        .input-group>.input-group-append:not(:last-child)>.input-group-text,
        .input-group>.input-group-prepend>.btn,
        .input-group>.input-group-prepend>.input-group-text {
           font-weight: 600; 
        }
        .form-control {
            border: 1px dashed;
            border-radius: 0%;
        }

        .custom-select {
            border: 1px dashed;
            border-radius: 0%;
        }

        .custom-select {
            width: 214.39px;
            display: inline;

        }

        .btn-primary {
            margin: 20px auto;
            border: 1px dashed black;
            border-radius: 0%;
            background-color: #fff;
            color: black;
            height: 39px;
            width: 100px;
            display: block;
        }

        .btn-primary:hover {
            background-color: #fff;
            border: 1px solid;
            color: black;

        }
        
        .orders {
            border: 1px dashed;
            margin-top: 60px;
        }
        .table thead th {
             border-bottom: 1px dashed #343a40;
        }
        .table td, .table th {
            border-top: none;<!doctype html>
<html lang="en">

<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
            integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
            crossorigin="anonymous"></script>

    <title>A24</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link
            href="https://fonts.googleapis.com/css2?family=Noto+Serif+KR:wght@500&family=Raleway:ital,wght@1,200;1,400;1,800&display=swap"
            rel="stylesheet">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Gothic+A1:wght@100&display=swap" rel="stylesheet">

    <style>
        * {
            font-family: 'Noto Serif KR', serif;
            font-family: 'Raleway', sans-serif;
        }

        .img {
            width: 40%;
            height: 500px;
            margin: 50px auto;
            background-image: url(https://cdn.shopify.com/s/files/1/0023/3789/8540/products/The-Witch-book-share_e38e1f2a-6718-4a53-8a7a-855736967a21.jpg?v=1618330188%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//cdn.shopify.com/s/files/1/0023/3789/8540/products/The-Witch-book-share_e38e1f2a-6718-4a53-8a7a-855736967a21.jpg?v=1618330188%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//cdn.shopify.com/s/files/1/0023/3789/8540/products/The-Witch-book-share_e38e1f2a-6718-4a53-8a7a-855736967a21.jpg?v=1618330188);
            background-position: center;
            background-size: cover;
            border: 1px double;
            box-shadow: 3px 3px 0px rgb(107, 106, 106);
        }

        .description {
            width: 40%;
            margin: auto auto 50px;
        }

        .title {
            font-size: 35px;
            font-weight: 800;
        }

        .price {
            font-size: 20px;
            font-weight: 400;
            margin: 0 0 0 5px;
        }

        .text {
            margin: 25px auto;
            color: rgb(140, 139, 139);
            font-weight: 200;
        }

        .order {
            width: 40%;
            margin: auto auto 100px auto;
        }

        .input-group-text {
            color: black;
            background-color: #fff;
            border: 1px dashed;
            border-radius: 0%;
            width: 100px;
            text-align: center;
            padding-left: 20px;
        }
        .input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),
        .input-group>.input-group-append:last-child>.input-group-text:not(:last-child),
        .input-group>.input-group-append:not(:last-child)>.btn,
        .input-group>.input-group-append:not(:last-child)>.input-group-text,
        .input-group>.input-group-prepend>.btn,
        .input-group>.input-group-prepend>.input-group-text {
           font-weight: 600;
        }
        .form-control {
            border: 1px dashed;
            border-radius: 0%;
        }

        .custom-select {
            border: 1px dashed;
            border-radius: 0%;
        }

        .custom-select {
            width: 214.39px;
            display: inline;

        }

        .btn-primary {
            margin: 20px auto;
            border: 1px dashed black;
            border-radius: 0%;
            background-color: #fff;
            color: black;
            height: 39px;
            width: 100px;
            display: block;
        }

        .btn-primary:hover {
            background-color: #fff;
            border: 1px solid;
            color: black;

        }

        .orders {
            border: 1px dashed;
            margin-top: 60px;
        }

        .table {
            text-align: center;}
        }
        .table thead th {
             border-bottom: 1px dashed #343a40;
        }
        .table td, .table th {
            border-top: none;
            text-align: center;
        }


    </style>

    <script>

       $(document).ready(function() {
            order_listing();
        });


        function order_listing() {
            // 주문목록 보기 API 연결
				$.ajax({
                type: "GET",
                url: "/order",
                data: {},
                success: function (response) {
                    let orders = response['all_orders']
                        for (let i = 0; i < orders.length; i++) {
                            let name = orders[i]['name']
                            let options = orders[i]['options']
                            let phone = orders[i]['phone']
                            let address = orders[i]['address']

                            let temp_html = `
                                 <tr>
                                    <td>${name}</td>
                                    <td>${options}</td>
                                    <td>${phone}</td>
                                    <td>${address}</td>
                                </tr>`
                            $('#orders-box').append(temp_html)
                    }
                }
            })
        }


        function order() {
            // 주문하기 API 연결
            let name = $('#inputname').val();
            let phone = $('#inputphone').val();
            let address = $('#inputaddress').val();
            let options = $('#inputGroupSelect01').val();

            $.ajax({
                type: "POST",
                url: "/order",
                data: {name_give: name, phone_give: phone, address_give: address, options_give: options},
                success: function (response) { // 성공하면
                    alert(response["msg"]);
                }
            })
        }

    </script>

    <script>
        $(document).ready(function () {
            won();
        });

        function won() {
            $.ajax({
                type: "GET",
                url: "http://spartacodingclub.shop/sparta_api/rate",
                data: {},
                success: function (response) {
                    let won = response['rate']
                    $('#rate').append(won)
                }
            })
        }
    </script>
</head>

<body>
<div class="img"></div>
<div class="description">
    <span class="title">The Witch Screenplay Book</span>
    <span class="price">$60</span>
    <h4>written and directed by Robert Eggers</h4>
    <span id="rate"><span onclick="won()"></span></span>
    <p class="text">
        Hardcover / 236 pages / 8 x 11 in.<br><br>
        Book 002 in the Screenplay Collection features Robert Eggers' meticulously researched script for The Witch ,
        along with short fiction by Carmen Maria Machado, a conversation between Eggers and scholar David D. Hall,
        and Eggers' own production sketches for the film.
    </p>
</div>
<div class="order">
    <div class="input-group mb-3">
        <div class="input-group-prepend">
            <span class="input-group-text" id="inputGroup-sizing-default">Name</span>
        </div>
        <input id="inputname" type="text" class="form-control" aria-label="Default"
               aria-describedby="inputGroup-sizing-default">
    </div>

    <div class="input-group mb-3">
        <div class="input-group-prepend">
            <span class="input-group-text" id="inputGroup-sizing-default">Phone</span>
        </div>
        <input id="inputphone" type="text" class="form-control" aria-label="Default"
               aria-describedby="inputGroup-sizing-default">
    </div>

    <div class="input-group mb-3">
        <div class="input-group-prepend">
            <span class="input-group-text" id="inputGroup-sizing-default">Address</span>
        </div>
        <input id="inputaddress" type="text" class="form-control" aria-label="Default"
               aria-describedby="inputGroup-sizing-default">
    </div>

    <div class="input-group mb-3">
        <div class="input-group-prepend">
            <label class="input-group-text" for="inputGroupSelect01">Options</label>
        </div>
        <select class="custom-select" id="inputGroupSelect01">
            <option selected>Please choose one.</option>
            <option value="1">Standard Edition</option>
            <option value="2">Limited Edition</option>
            <option value="3">Director's Edition</option>
        </select>
    </div>

    <button onclick="order()" type="button" class="btn btn-primary mybtn">Buy Now</button>

                <div class="orders">
                <table class="table">
                    <thead>
                    <tr>
                        <th scope="col">name</th>
                        <th scope="col">options</th>
                        <th scope="col">phone</th>
                        <th scope="col">address</th>
                    </tr>
                    </thead>
                    <tbody id="orders-box"></tbody>
                </table>
            </div>

</div>
</body>

</html>

サマリ


1.クライアントとサーバの確認
2.サーバの作成
3.クライアントの作成
4.完了確認
POSTもGETも同じように処理します!

プロセスの詳細


Ⅰ.デフォルト設定


▸static,templatesフォルダ、app.pyファイルの作成
▸templatesのindex.htmlの生成
インストール▸flask、pymongo(スクロールが必要な場合はbs 4もインストール要求もインストール)

Ⅱ.Buy Now(POST API)


Ⅱ-1.クライアント(index.html)


リクエスト
<button onclick="order()" type="button" class="btn btn-primary mybtn">Buy Now</button>
ボタンをクリックして、order関数としてサーバを要求します.
➋ ajax
  • index.html
  • function order() {
                $.ajax({
                    type: "POST",
                    url: "/order",
                    data: {},
                    success: function (response) { // 성공하면
                        alert(response["msg"]);
                    }
                })
            }
  • app.py
    @app.route('/order', methods=['POST'])
  • サーバのPOSTが/orderを受信しているため、クライアントの機能もurlで/orderを受信します.
    接続の確認
  • app.py
    return jsonify({'result': 'success', 'msg': 'Thank you for your order!'})
  • 「Buy Now」ボタンをクリックすると、次のalertウィンドウが表示されます.

    Ⅱ-2.サーバ(app.py)


    DB insert
  • app.py
  • @app.route('/order', methods=['POST'])
    def save_order():
        name_receive = request.form['name_give']
        phone_receive = request.form['phone_give']
        address_receive = request.form['address_give']
        options_receive = request.form['options_give']
    	doc = {
            'name':name_receive,
            'phone':phone_receive,
            'address':address_receive,
            'options':options_receive
        }
        db.thewitch.insert_one(doc)
    サーバからDBに赤い入力値を挿入します.
    [スパルタコードクラブ]ネットワーク開発総合クラス-第3週(3)を参照してください.

    Ⅱ-3.クライアント→サーバ


    ➊input値の指定
  • body
  • <div class="order">
        <div class="input-group mb-3">
            <div class="input-group-prepend">
                <span class="input-group-text" id="inputGroup-sizing-default">Name</span>
            </div>
            <input id="inputname" type="text" class="form-control" aria-label="Default"
                   aria-describedby="inputGroup-sizing-default">
        </div>
        <div class="input-group mb-3">
            <div class="input-group-prepend">
                <span class="input-group-text" id="inputGroup-sizing-default">Phone</span>
            </div>
            <input id="inputphone" type="text" class="form-control" aria-label="Default"
                   aria-describedby="inputGroup-sizing-default">
        </div>
        <div class="input-group mb-3">
            <div class="input-group-prepend">
                <span class="input-group-text" id="inputGroup-sizing-default">Address</span>
            </div>
            <input id="inputaddress" type="text" class="form-control" aria-label="Default"
                   aria-describedby="inputGroup-sizing-default">
        </div>
        <div class="input-group mb-3">
            <div class="input-group-prepend">
                <label class="input-group-text" for="inputGroupSelect01">Options</label>
            </div>
            <select class="custom-select" id="inputGroupSelect01">
                <option selected>Please choose one.</option>
                <option value="1">Standard Edition</option>
                <option value="2">Limited Edition</option>
                <option value="3">Director's Edition</option>
            </select>
        </div>
  • script
  •         function order() {
                let name = $('#inputname').val();
                let phone = $('#inputphone').val();
                let address = $('#inputaddress').val();
                let options = $('#inputGroupSelect01').val();
    関数では、サーバ構築のDBにinput値を送信し、変数として指定するために、inputごとにidを付与します.
    この場合、「Option」の値はinputではなくcustom-selectであるため、idはcustom-selectに付与される.
    入力値の送信
  • app.py
  • @app.route('/order', methods=['POST'])
    def save_order():
        name_receive = request.form['name_give']
        phone_receive = request.form['phone_give']
        address_receive = request.form['address_give']
        options_receive = request.form['options_give']
  • index.html
  •             $.ajax({
                    type: "POST",
                    url: "/order",
                    data: {name_give: name, phone_give: phone, address_give: address, options_give: options},
                    success: function (response) {
                        alert(response["msg"]);
                    }
                })
            }
    上で指定した変数をajaxで転送
    dateは、サーバが受信を決定する名前と一致する必要があります.
    を送信するかどうかは、Robo 3 Tを参照してください.

    Ⅲ.注文履歴の呼び出し(GET API)


    Ⅲ-1.サーバ-クライアント接続


    ➊クライアント位置決定
           $(document).ready(function() {
                order_listing();
            });
    APIをready関数に接続します.ページのロード後、すぐにデータをロードする必要があるためです.
    ➋ ajax
    function order_listing() {
    		$.ajax({
            type: "GET",
            url: "/order",
            data: {},
            success: function (response) {
                if (response["result"] == "success") {
                	alert(response["msg"]);
                }
            }
        })
    }        
    GET API要求データ照会であるため、日付値は空です.
    接続の確認
           $(document).ready(function() {
                order_listing();
            });
    ⬇︎
    function order_listing() {
    		$.ajax({
            type: "GET",
            url: "/order",
            data: {},
            success: function (response) {
                if (response["result"] == "success") {
                	alert(response["msg"]);
                }
            }
        })
    } 
    ⬇︎
    @app.route('/order', methods=['GET'])
    def view_orders():
        orders = list(db.thewitch.find({}, {'_id': False}))
        return jsonify({'msg': '이 요청은 GET!'})
    クライアントのready関数が動作し、order listingが呼び出されます.
    クライアント要求を受信したサーバ呼び出しメッセージ.
    が正常に呼び出されるとalertウィンドウが停止します

    Ⅲ-2.サーバー

    @app.route('/order', methods=['GET'])
    def view_orders():
        orders = list(db.thewitch.find({}, {'_id': False}))
        return jsonify({'all_orders': orders})
    POST生成DBをロードして戻る
    「ordersは変数にすぎません.」重要なのはall ordersを降ろすことです.

    Ⅲ-3.クライアント


    ドア.
    function order_listing() {
    	$.ajax({
              type: "GET",
              url: "/order",
              data: {},
              success: function (response) {
                   let orders = response['all_orders']
                       for (let i = 0; i < orders.length; i++) {
                           let name = orders[i]['name']
                           let options = orders[i]['options']
                           let phone = orders[i]['phone']
                           let address = orders[i]['address']
    
    サーバからダウンロードした「all orders」をfor文と呼ぶ
    ❷ orders table
  • HTML
  • <div class="orders">
        <table class="table">
               <thead>
                    <tr>
                        <th scope="col">name</th>
                        <th scope="col">options</th>
                        <th scope="col">phone</th>
                        <th scope="col">address</th>
                    </tr>
                </thead>
        		<tbody>
                        <tr>
                          <td>1</th>
                          <td>Mark</td>
                          <td>Otto</td>
                          <td>@mdo</td>
                                .
                                .
                                .
                         </tr>       
     		</tbody>
        </table>
    </div>
    Bootstrap Tablesを使用して、Buy Now Buttonの下に受注履歴を送信するテーブルを挿入します.
  • CSS
  •         .orders {
                border: 1px dashed;
                margin-top: 60px;
            }
    tableとbuttonの間にtableを間隔するordersにmarify-topを与える
    ❸ temp_html
  • body
  •     		<tbody id="orders-box">
                        <tr>
                          <td>1</th>
                          <td>Mark</td>
                          <td>Otto</td>
                          <td>@mdo</td>
                                .
                                .
                                .
                         </tr>       
     		</tbody>
    temp htmlを送信するtbodyにidを指定する
  • script
  • let temp_html = `
        <tr>
             <td>${name}</td>
             <td>${options}</td>
             <td>${phone}</td>
             <td>${address}</td>
        </tr>`
    $('#orders-box').append(temp_html)
    tableの内容をtemp htmlに挿入する
    既存の値をfor文の変数に変更
    Orders-boxでappend(temp html)に送信

    予期せぬ待ち伏せCSS



    智旻の下だけにこれだけのスペースを残すのは嫌なので、この部分の空白を取り除きたいのですが、あらゆる方法を使い果たしていても、その部分は思うようにコントロールできません…
    last-childを選択!「ヘッダー」を同時に選択
    heightを指定します!テーブルが割れた
    それ以外は何度も試したが、結果は同じように失敗した.😞
    よくできていて、CSSでブロックされたとは思わなかった...OTL
    空白を何とか減らそうとしたが、今回の課題のポイントはCSSではないので、そのまま提出した.この部分は個人的に勉強しなければならない.
    挑戦試合で授業が遅れたので、今回はミスを詳しく記録できなかった.そこで,処理手順を詳細に記録した.今回の作業は書評例題コードをそのまま応用すればいいだけなので、作業難易度自体は難しくはありませんが、すぐにまた誤りの原因が何なのか訂正し直すので、ちょっと気分が悪いです.私は一人でノートでもう一度練習します.
    ちなみにブログもnotionのように切り替え機能を持ってほしいです.完全なコードを一度添付すれば、遊んでいるわけではありません.