FlaskにVue.jsからGET、POSTする方法

ファイル構造はシンプルにこのようにしています。

```
.
├── app.py
└── templates
    └── index.html
```


# GETする方法

productsが取得したいデータになっています。
jsonifyを使って、JSONデータを返しています。

portは適当につけてます。

```py:app.py
from flask import Flask, render_template, jsonify, request

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False # 文字化け防止

products = [
    {'id': 1, 'name':'商品1', 'price':780},
    {'id': 2, 'name':'商品2', 'price':1280},
    {'id': 3, 'name':'商品3', 'price':1980},
]

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

@app.route('/products')
def get_products():
    return jsonify({'products': products}) # JSONファイルにしてデータを返す

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

デミレータは今回は使っていないですが、
こちらの記事を参考につけています。
[!Flask で Vue.js を使おうと思ったら出鼻をくじかれたのでメモ](https://qiita.com/guromityan/items/5846fcefd87abcf76f7f)

GETではつけていないですが、次に紹介するPOSTの方ではdelimiterを使ってレンダリングさせています。
(レンダリングの使い方あってるのか不明)

```html:templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    h2 {
      margin-bottom: 0;
    }
  </style>
  <title>Document</title>
</head>
<body>
  <div id="app">
    <h2>GET</h2>
    <button @click="getProducts">GET /products</button>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      delimiters: ['[[', ']]'],
      methods: {
        getProducts: function() {
          axios.get('/products')
            .then(response => {
              for (const product of response.data.products) {
                console.log(product);
              }
            })
            .catch(error => {
              console.log(error);
            })
          }
  </script>
</body>
</html>
```

GETのボタンをクリックすると下記のようにログが出力されます。

![2021-08-19_18h33_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428239/df9373ec-0a33-02b6-1981-0d41ad77a1ef.png)


# POSTの実行

今度は商品追加(Create)のAPI,create_product()を作成します。


```py:app.py
from flask import Flask, render_template, jsonify, request

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False

products = [
    {'id': 1, 'name':'商品1', 'price':780},
    {'id': 2, 'name':'商品2', 'price':1280},
    {'id': 3, 'name':'商品3', 'price':1980},
]

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

@app.route('/product', methods=['POST'])
def create_product():
    post_data = request.get_json() # get_json()でデータの受け取り

    new_product = {'id': products[-1]['id']+1}  # idのみの辞書を作成
    new_product.update(list(post_data.items())) # 上記で作成した辞書にpostで受けたデータを追加(update)

    products.append(new_product)
    return jsonify({'products': products})

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



```html:templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    h2 {
      margin-bottom: 0;
    }
  </style>
  <title>Document</title>
</head>
<body>
  <div id="app" class="container">
    <h2>POST</h2>
    <form @submit.prevent="createProduct">
      <label>商品名
      <input type="text" v-model="newProduct.name">
      </label><br>
      <label>価格　
        <input type="number" v-model.number="newProduct.price"> <!-- 数値データになるようにnumber修飾性をつける -->
      </label><br>
      <input type="submit" value="POST /product">
    </form>
    <br>
    [[ newProduct.name ]]<br> <!-- バインド情報の表示 -->
    [[ newProduct.price ]]
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      delimiters: ['[[', ']]'],
      data: {
        newProduct: {name: '', price: null},
      },
      methods: {
          createProduct: function() {
            axios.post('/product', this.newProduct)
              .then(response => {
                for (const product of response.data.products) {
                  console.log(product);
                }
              })
              .catch(error => {
                console.log(error);
              })
          }
        }
    });
  </script>
</body>
</html>
```

POSTボタンの下にバインドしたデータも表示されています。
ログでは商品が追加されてかえってきているので、うまくいっています。

![2021-08-19_18h48_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428239/409c6881-6e3d-0fd7-5c0e-e9696a681a59.png)


# はまったこと

methodsの中でメソッドをアロー関数で書こうとしたら、動かなかったです。


```js
// これは動く
methods: function() {}

// アロー関数だと動かない
methods: () => {}
```



# 参考情報
axiosのGitHub、Exampleは見やすかったです。

[!Vue.jsとFlaskの間でフォーム情報をやり取りする](https://kivantium.hateblo.jp/entry/2018/07/23/173511)
[!axios GitHub](https://github.com/axios/axios#example)