使用Flask和Vue.js开发一个单页面应用程序(三)

2019-05-24 16:44:03 浏览数 (1)

点击蓝字关注△ 回复“1024”领取福利大礼包

接上回,现在我们开始构建CRUD应用程序。

我们的目标是设计一个后端RESTful API,由Python和Flask提供支持。API本身应该遵循RESTful设计原则,使用基本的HTTP请求方式:GET、POST、PUT和DELETE来完成。

我们还将用Vue开发一个前端应用程序,使用后端提供的接口API:

添加一个GET请求的接口服务

在app.py中,添加一个书籍列表,这是一些假数据,真实情况应该从数据库获取:

代码语言:javascript复制
BOOKS = [
    {
        'title': 'On the Road',
        'author': 'Jack Kerouac',
        'read': True
    },
    {
        'title': 'Harry Potter and the Philosopher's Stone',
        'author': 'J. K. Rowling',
        'read': False
    },
    {
        'title': 'Green Eggs and Ham',
        'author': 'Dr. Seuss',
        'read': True
    }
]

在后端程序app.py文件中,添加访问路由:

代码语言:javascript复制
@app.route('/books', methods=['GET'])
def all_books():
    return jsonify({
        'status': 'success',
        'books': BOOKS
    })

运行flask服务,在浏览器中访问http://127.0.0.1:5000/books接口,将看到:

代码语言:javascript复制
{
    "books": [
        {
            "author": "Jack Kerouac",
            "read": true,
            "title": "On the Road"
        },
        {
            "author": "J. K. Rowling",
            "read": false,
            "title": "Harry Potter and the Philosopher's Stone"
        },
        {
            "author": "Dr. Seuss",
            "read": true,
            "title": "Green Eggs and Ham"
        }
    ],
    "status": "success"
}

更新Books.vue组件:

代码语言:javascript复制
<template>
  <div class="container">
    <div class="row">
      <div class="col-sm-10">
        <h1>Books</h1>
        <hr><br><br>
        <button type="button" class="btn btn-success btn-sm">Add Book</button>
        <br><br>
        <table class="table table-hover">
          <thead>
            <tr>
              <th scope="col">Title</th>
              <th scope="col">Author</th>
              <th scope="col">Read?</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(book, index) in books" :key="index">
              <td>{{ book.title }}</td>
              <td>{{ book.author }}</td>
              <td>
                <span v-if="book.read">Yes</span>
                <span v-else>No</span>
              </td>
              <td>
                <button type="button" class="btn btn-warning btn-sm">Update</button>
                <button type="button" class="btn btn-danger btn-sm">Delete</button>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      books: [],
    };
  },
  methods: {
    getBooks() {
      const path = 'http://localhost:5000/books';
      axios.get(path)
        .then((res) => {
          this.books = res.data.books;
        })
        .catch((error) => {
          // eslint-disable-next-line
          console.error(error);
        });
    },
  },
  created() {
    this.getBooks();
  },
};
</script>

现在运行前端程序:

代码语言:javascript复制
$ npm run serve

打开浏览器访问:http://127.0.0.1:8080,你将看到:

接下来,我们将使用一个模态组件来添加一本新书。我们将在前端程序中安装Bootstrap Vue库,它提供了一组使用基于Bootstrap的HTML和CSS样式设计的Vue组件。

首先安装bootstrap-vue库:

代码语言:javascript复制
$ npm install bootstrap-vue@2.0.0-rc.11 --save

在client/src/main.js中添加Bootstrap Vue库:

代码语言:javascript复制
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'bootstrap/dist/css/bootstrap.css'
import BootstrapVue from 'bootstrap-vue';

Vue.config.productionTip = false
Vue.use(BootstrapVue);

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
添加一个POST请求的接口服务

在app.py文件中,增加一个POST请求,用来完成添加一个本书的功能:

代码语言:javascript复制
@app.route('/books', methods=['GET', 'POST'])
def all_books():
    response_object = {'status': 'success'}
    if request.method == 'POST':
        post_data = request.get_json()
        BOOKS.append({
            'title': post_data.get('title'),
            'author': post_data.get('author'),
            'read': post_data.get('read')
        })
        response_object['message'] = 'Book added!'
    else:
        response_object['books'] = BOOKS
    return jsonify(response_object)

更新imports:

代码语言:javascript复制
from flask import Flask, jsonify, request

更新前端程序中Books.vue文件,最终应该是下面这样:

代码语言:javascript复制
<template>
  <div class="container">
    <div class="row">
      <div class="col-sm-10">
        <h1>Books</h1>
        <hr><br><br>
        <button type="button" class="btn btn-success btn-sm" v-b-modal.book-modal>Add Book</button>
        <br><br>
        <table class="table table-hover">
          <thead>
            <tr>
              <th scope="col">Title</th>
              <th scope="col">Author</th>
              <th scope="col">Read?</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(book, index) in books" :key="index">
              <td>{{ book.title }}</td>
              <td>{{ book.author }}</td>
              <td>
                <span v-if="book.read">Yes</span>
                <span v-else>No</span>
              </td>
              <td>
                <button type="button" class="btn btn-warning btn-sm">Update</button>
                <button type="button" class="btn btn-danger btn-sm">Delete</button>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
    <b-modal ref="addBookModal"
             id="book-modal"
             title="Add a new book"
             hide-footer>
      <b-form @submit="onSubmit" @reset="onReset" class="w-100">
      <b-form-group id="form-title-group"
                    label="Title:"
                    label-for="form-title-input">
          <b-form-input id="form-title-input"
                        type="text"
                        v-model="addBookForm.title"
                        required
                        placeholder="Enter title">
          </b-form-input>
        </b-form-group>
        <b-form-group id="form-author-group"
                      label="Author:"
                      label-for="form-author-input">
            <b-form-input id="form-author-input"
                          type="text"
                          v-model="addBookForm.author"
                          required
                          placeholder="Enter author">
            </b-form-input>
          </b-form-group>
        <b-form-group id="form-read-group">
          <b-form-checkbox-group v-model="addBookForm.read" id="form-checks">
            <b-form-checkbox value="true">Read?</b-form-checkbox>
          </b-form-checkbox-group>
        </b-form-group>
        <b-button type="submit" variant="primary">Submit</b-button>
        <b-button type="reset" variant="danger">Reset</b-button>
      </b-form>
    </b-modal>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      books: [],
      addBookForm: {
        title: '',
        author: '',
        read: [],
      },
    };
  },
  methods: {
    getBooks() {
      const path = 'http://localhost:5000/books';
      axios.get(path)
        .then((res) => {
          this.books = res.data.books;
        })
        .catch((error) => {
          // eslint-disable-next-line
          console.error(error);
        });
    },
    addBook(payload) {
      const path = 'http://localhost:5000/books';
      axios.post(path, payload)
        .then(() => {
          this.getBooks();
        })
        .catch((error) => {
          // eslint-disable-next-line
          console.log(error);
          this.getBooks();
        });
    },
    initForm() {
      this.addBookForm.title = '';
      this.addBookForm.author = '';
      this.addBookForm.read = [];
    },
    onSubmit(evt) {
      evt.preventDefault();
      this.$refs.addBookModal.hide();
      let read = false;
      if (this.addBookForm.read[0]) read = true;
      const payload = {
        title: this.addBookForm.title,
        author: this.addBookForm.author,
        read, // property shorthand
      };
      this.addBook(payload);
      this.initForm();
    },
    onReset(evt) {
      evt.preventDefault();
      this.$refs.addBookModal.hide();
      this.initForm();
    },
  },
  created() {
    this.getBooks();
  },
};
</script>

现在运行前后端程序,让我们添加一本书试试。点击’add book’按钮,将看到:

接下来,让我们添加一个提示组件,以便在添加新书之后向最终用户显示一条消息。我们将为此创建一个新的组件,因为您可能会在许多组件中使用该功能。

添加一个名为Alert.vue的新文件,在”client/src/components”目录下:

代码语言:javascript复制
<template>
  <p>It works!</p>
</template>

然后把Alert组件添加到Books组件的JavaScript脚本部分添加该组件:

代码语言:javascript复制
<script>
import axios from 'axios';
import Alert from './Alert';

...

export default {
  data() {
    return {
      books: [],
      addBookForm: {
        title: '',
        author: '',
        read: [],
      },
    };
  },
  components: {
    alert: Alert,
  },

  ...

};
</script>

然后,我们就可以在template部分引入组件,就正常使用了。

代码语言:javascript复制
<template>
  <b-container>
    <b-row>
      <b-col col sm="10">
        <h1>Books</h1>
        <hr><br><br>
        <alert></alert>
        <button type="button" class="btn btn-success btn-sm" v-b-modal.book-modal>Add Book</button>

        ...

      </b-col>
    </b-row>
  </b-container>
</template>

现在刷新浏览器,你将看到:

现在重写Alert.vue组件,具体内容:

代码语言:javascript复制
<template>
  <div>
    <b-alert variant="success" show>{{ message }}</b-alert>
    <br>
  </div>
</template>

<script>
export default {
  props: ['message'],
};
</script>

然后在Books.vue组件中,重写一下alert组件,具体内容:

代码语言:javascript复制
<alert message="hi"></alert>

现在刷新一下浏览器,看看效果吧。不出错的话,应该是下面这样的。

添加的提醒组件,是添加书籍成功后,给出提示。但是目前是一直显示在页面上的。所有我们需要再处理一下。

首先,在Books.vue组件的data中,添加两个数据分别为message、showMessage。具体如下:

代码语言:javascript复制
data() {
  return {
    books: [],
    addBookForm: {
      title: '',
      author: '',
      read: [],
    },
    message: '',
    showMessage: false,
  };
},

然后,在Books.vue组件的addBook方法中,控制message的内容和是否显示。具体如下:

代码语言:javascript复制
addBook(payload) {
  const path = 'http://localhost:5000/books';
  axios.post(path, payload)
    .then(() => {
      this.getBooks();
      this.message = 'Book added!';
      this.showMessage = true;
    })
    .catch((error) => {
      // eslint-disable-next-line
      console.log(error);
      this.getBooks();
    });
},

最后,需要在引用的alert组件上,加上v-if控制组件是显示。具体如下:

代码语言:javascript复制
<alert :message=message v-if="showMessage"></alert>

现在可以去刷新浏览器,添加一本新书,看看效果。未完待续,明天继续分享更新和删除书籍两个功能。

如果觉得内容还不错,分享给更多朋友,一起提升编程技能。

0 人点赞