日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > vue >内容正文

vue

[Flask+Vue]Books全栈应用

發布時間:2025/3/13 vue 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [Flask+Vue]Books全栈应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Flask和Vue.js構建全棧單頁面web應用【通過Flask開發RESTful API】

路小飛

退乎(假的)

?關注

146 人贊同了該文章

前言

看了一些國外的關于介紹flask和vue的前后端分離的文章,但沒看到比較通俗易懂,代碼完善的,直到昨天看到一篇新出的文章,而且內容非常棒,所以翻譯過來,供大家一起學習。

原文來自Developing a CRUD App with Flask and Vue.js


正文:

下面會逐步演示通過Flask和Vue如何完成一個基本的CRUD應用程序。我們將從搭建框架開始,使用Vue CLI構建一個新的Vue應用程序,然后通過Python和Flask開發的RESTful API執行基本的CRUD操作。

主要依賴的庫包括:

  • Vue v2.6.10
  • Vue CLI v3.7.0
  • Node v12.1.0
  • npm v6.9.0
  • Flask v1.0.2
  • Python v3.7.3

本文章的目標

在文章結束時,你將能夠知道:

  • 什么是Flask
  • 什么是Vue,以及它和其他的UI庫或者前端框架有什么不同(React和Angular)
  • 使用Vue CLI搭建一個Vue項目
  • 在瀏覽器中創建和提交一個Vue組件
  • 通過Vue組件創建一個單頁面應用
  • 連接Vue應用和Flask后端
  • 通過Flask開發RESTful API
  • 使用 Bootstrap給Vue組件添加樣式
  • 使用 Vue Router 創建路由和渲染組件
  • 什么是Flask?

    Flask是一個簡單但功能強大的Python微Web框架,非常適合構建RESTful API。像Sinatra(Ruby)和Express(Node)一樣,它非常小而且很靈活,所以你可以先開始一個小型的應用,并在它的基礎上根據需求建立更加復雜的應用程序。

    如果是第一次使用Flask,可以參考以下兩個學習資源:

  • Flaskr TDD
  • Node開發者的Flask
  • 什么是Vue?

    VUE是一個開源JavaScript框架,用于構建用戶界面。它采用了React和Angular方面的一些最佳做法。也就是說,與React和Angular相比,它更平易近人,所以初學者可以快速地開始和運用Vue。它同樣很強大,提供了創建最新前端應用程序所需要的所有功能。

    有關Vue的更多信息,以及它與React和Angular的各種優缺點,可以參閱以下文章:

  • VUE:與其他框架的比較
  • React vs Angular vs Vue.js:一個完整的比較指南
  • React vs Angular vs Vue:2017年的比較
  • 第一次用Vue,可以花些時間學習一遍官方的Vue指南。

    Flask安裝

    首先新建一個文件夾:

    $ mkdir flask-vue-crud $ cd flask-vue-crud

    接下來,為這個目錄創建一個虛擬環境,創建虛擬環境的方式因不同的開發環境可能存在不同。

    $ python3.7 -m venv env $ source env/bin/activate

    安裝Flask和和Flask-CORS擴展。

    (env)$ pip install Flask==1.0.2 Flask-Cors==3.0.7

    在根目錄下新建server文件夾,并在文件夾中創建一個app.py文件:

    from flask import Flask, jsonify from flask_cors import CORS# configuration DEBUG = True# instantiate the app app = Flask(__name__) app.config.from_object(__name__)# enable CORS CORS(app, resources={r'/*': {'origins': '*'}})# sanity check route @app.route('/ping', methods=['GET']) def ping_pong():return jsonify('pong!')if __name__ == '__main__':app.run()

    為什么要用Flask-CORS擴展呢?是為了發出跨域請求——比如,來自不同協議,IP地址,域名或端口的請求——而Flask-CORS可以幫我們處理這些。

    需要注意的是,雖然上面的設置允許所有路由上的跨域請求(來自任何域、協議或端口)。但在生產環境中,您應該只允許來自托管前端應用程序的域的跨域請求。有關此問題的更多信息,請參閱Flask-CORS文檔。

    運行app:

    (env)$ python server/app.py

    現在可以用瀏覽器登錄http://localhost:5000/ping來測試了,你會看到一個json格式的

    "pong!"

    回到終端中,按Ctrl+C鍵關閉服務器。現在,我們就可以把注意力轉向前端,開始設置Vue。

    VUE設置

    我們將使用強大的Vue CLI工具來生成一個自定義項目樣板。

    在全局內安裝Vue CLI:

    $ npm install -g @vue/cli@3.7.0 第一次使用npm,可以查閱About npm指南。

    安裝完成后,用下面的命令來初始化一個名為client的Vue項目:

    $ vue create client

    接下來,需要回答一些關于項目的問題。在此項目中,具體的選擇如下所示:

    Vue CLI v3.7.0 ? Please pick a preset: Manually select features ? Check the features needed for your project:? Babel? TypeScript? Progressive Web App (PWA) Support ?? Router? Vuex? CSS Pre-processors? Linter / Formatter? Unit Testing? E2E Testing ? Use history mode for router? Yes ? Pick a linter / formatter config: Airbnb ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In package.json ? Save this as a preset for future projects? (y/N) No

    等待創建完畢后,項目根目錄下多出一個client文件夾。里面有很多的內容,不過我們只需要處理其中‘src’文件夾內的一些內容以及‘public’文件夾內的index.html文件,其他的不需要我們操作。

    index.html文件是Vue應用的起點。

    src文件夾內的文件結構如下:

    ├── App.vue ├── assets │ └── logo.png ├── components │ └── HelloWorld.vue ├── main.js ├── router.js └── views├── About.vue└── Home.vue

    詳解:

    • main.js?app入口點,它與根組件一起加載和初始化Vue。
    • app.vue?根組件,它是開始渲染所有其他組件時的起點。
    • 'components'?存儲UI組件
    • router.js?定義URL并將URL映射到對應的組件
    • 'views'?存儲綁定到路由器的UI組件
    • 'asset'?存儲靜態資源,如圖像和字體

    打開/client/src/components/HelloWorld.vue文件。這是一個單文件組件,它包括三個部分:

  • template: 用于組件特定的HTML部分
  • Script:通過JavaScript實現組件邏輯的地方
  • style: 用于CSS樣式
  • 現在開始運行開發服務器:

    $ cd client $ npm run serve

    為了簡化項目,我們可以刪除views文件夾,然后添加一個名為ping.vue的文件到Client/src/Components文件夾下。

    <template><div><p>{{ msg }}</p></div> </template><script> export default {name: 'Ping',data() {return {msg: 'Hello!',};}, }; </script>

    然后更新Client/src/router.js,將“/ping”映射到ping組件,如下所示:

    import Vue from 'vue'; import Router from 'vue-router'; import Ping from './components/Ping.vue';Vue.use(Router);export default new Router({mode: 'history',base: process.env.BASE_URL,routes: [{path: '/ping',name: 'Ping',component: Ping,}], });

    最后,刪除Client/src/App.vue中template部分的導航欄,變為如下所示:

    <template><div id="app"><router-view/></div> </template>

    現在,你可以通過瀏覽器登錄http://localhost:8080/ping看到 hello! 了。

    要將客戶端Vue應用程序與后端Flask應用程序連接起來,我們可以使用axios庫發送Ajax請求。

    首先安裝對應庫:

    $ npm install axios@0.18.0 --save

    在ping.vue中更新組件的script部分,如下所示:

    <script> import axios from 'axios';export default {name: 'Ping',data() {return {msg: '',};},methods: {getMessage() {const path = 'http://localhost:5000/ping';axios.get(path).then((res) => {this.msg = res.data;}).catch((error) => {// eslint-disable-next-lineconsole.error(error);});},},created() {this.getMessage();}, }; </script>

    在一個新的終端窗口中啟動Flask應用程序。你可以看到http://localhost:8080/ping頁面不再是hello!而是pong!。實際上,當從后端返回響應時,我們將上面的msg設置為來自服務器響應對象的data的值。

    安裝Bootstrap

    接下來,讓我們將一個流行的CSS框架Bootstrap添加到應用程序中,這樣我們就可以快速地添加一些樣式。

    安裝:

    $ npm install bootstrap@4.3.1 --save 忽略jquery和popper.js的warnings警告。不要將它們添加到項目中。后面會詳細的解釋。

    將Bootstrap中的樣式導入到Client/src/main.js:

    import 'bootstrap/dist/css/bootstrap.css'; import Vue from 'vue'; import App from './App.vue'; import router from './router';Vue.config.productionTip = false;new Vue({router,render: h => h(App), }).$mount('#app');

    更新Client/src/App.vue中的style部分:

    <style> #app {margin-top: 60px } </style>

    通過使用ping組件中的Button和Container,確保Bootstrap能正確連接:

    <template><div class="container"><button type="button" class="btn btn-primary">{{ msg }}</button></div> </template>

    運行服務器:

    $ npm run serve

    你可以看到:

    接下來,新建一個Books.vue的新文件,并在其中添加一個Books組件:

    <template><div class="container"><p>books</p></div> </template>

    更新路由文件router.js:

    import Vue from 'vue'; import Router from 'vue-router'; import Books from './components/Books.vue'; import Ping from './components/Ping.vue';Vue.use(Router);export default new Router({mode: 'history',base: process.env.BASE_URL,routes: [{path: '/',name: 'Books',component: Books,},{path: '/ping',name: 'Ping',component: Ping,},], });

    測試:http://localhost:8080

    最后,讓我們將Bootstrap-styled表單添加到Books組件:

    <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><td>foo</td><td>bar</td><td>foobar</td><td><div class="btn-group" role="group"><button type="button" class="btn btn-warning btn-sm">Update</button><button type="button" class="btn btn-danger btn-sm">Delete</button></div></td></tr></tbody></table></div></div></div> </template>

    你現在應該看到:

    現在我們可以開始構建CRUD應用程序的功能了。

    我們要建什么?

    我們的目標是為books設計一個后端RESTful API,由Python和Flask實現。API本身應該遵循RESTful設計原則,并且可以使用基本的HTTP功能:GET、POST、PUT和DELETE。

    我們還將在后端API的基礎上使用Vue搭建完整的前端應用:

    本教程只討論快樂的構建之路,處理錯誤是一個單獨的練習。可以嘗試通過您的理解,自己在前端和后端添加適當的錯誤處理。

    GET 路由

    服務器端

    向server/app.py添加書籍列表:

    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.route('/books', methods=['GET']) def all_books():return jsonify({'status': 'success','books': BOOKS})

    運行flask app,并測試路由http://localhost:5000/books.

    想要進行更多的挑戰嗎?可以為這個程序編寫一個自動測試。查看這兒有更多關于測試Flask應用的資源信息。

    客戶端

    更新books組件:

    <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><div class="btn-group" role="group"><button type="button" class="btn btn-warning btn-sm">Update</button><button type="button" class="btn btn-danger btn-sm">Delete</button></div></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-lineconsole.error(error);});},},created() {this.getBooks();}, }; </script>

    初始化組件后,通過創建的生命周期鉤子來調用getBooks( )方法,該方法從我們剛剛設置的后端端點獲取書籍。

    查看實例的生命周期鉤子可以了解更多有關組件生命周期和可用方法的信息。

    在模板中,我們通過v-for指令遍歷圖書列表,在每次迭代中創建一個新的表行。索引值當做key使用。最后,v-if用于呈現“yes”或“no”,指示用戶是否已讀過書

    Bootstrap Vue

    在下一節中,我們將使用一個模式添加一本新書。我們將為此添加一個Bootstrap Vue庫,它提供了一組使用基于引導的HTML和CSS樣式的Vue組件。

    為什么要使用Bootstrap Vue庫?Bootstrap的modal組件使用的是jQuery,因此,您應該避免在同一個項目中Bootstrap與Vue一起使用,因為Vue使用的是虛擬DOM來更新DOM。換句話說,如果您使用jQuery操作DOM,Vue將無法知道這些操作。如果您一定要使用jQuery,至少不要在同一個DOM元素上同時使用Vue和jQuery。

    安裝:

    $ npm install bootstrap-vue@2.0.0-rc.19 --save

    在Client/src/main.js中啟用Bootstrap Vue庫:

    import 'bootstrap/dist/css/bootstrap.css'; import BootstrapVue from 'bootstrap-vue'; import Vue from 'vue'; import App from './App.vue'; import router from './router';Vue.use(BootstrapVue);Vue.config.productionTip = false;new Vue({router,render: h => h(App), }).$mount('#app');

    POST路由

    服務器端

    更新現在的路由處理程序,讓它支持處理POST請求,從而添加新的書籍:

    from flask import Flask, jsonify, request@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'] = BOOKSreturn jsonify(response_object)

    當Flask服務器運行時,您可以在一個新的終端選項卡中測試POST路由的功能:

    $ curl -X POST http://localhost:5000/books -d \'{"title": "1Q84", "author": "Haruki Murakami", "read": "true"}' \-H 'Content-Type: application/json'

    你可以看到:

    {"message": "Book added!","status": "success" }

    您還可以通過訪問http://localhost:5000/books端點查看響應中的是否成功添加了新書。

    如果標題已經存在怎么辦?或者,如果一個標題有一個以上的作者呢?你可以自己嘗試解決這些問題來檢測你的知識理解。還有,如何處理無效的數據體呢,比如在缺少title、author或read的情況下?

    客戶端

    讓我們現在在客戶端添加POST模式,以便將新書添加到Books組件中,先從HTML開始:

    <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"requiredplaceholder="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"requiredplaceholder="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>

    將它添加到最后結束的dev標簽之前。可以查看代碼。v-model是一個用于將輸入值綁定到對應狀態的指令。你很快就會看到這一點。

    hide-footer有什么用?想了解的話,可以在Bootstrap Vue文檔中查看這個對應文檔。

    更新script部分:

    <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-lineconsole.error(error);});},addBook(payload) {const path = 'http://localhost:5000/books';axios.post(path, payload).then(() => {this.getBooks();}).catch((error) => {// eslint-disable-next-lineconsole.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>

    這段代碼做了什么?

  • addBookForm 通過v-modal綁定到表單輸入。當其中一個被更新時,另一個也會被更新,這叫做雙向綁定。花點時間思考一下,你認為這會使狀態管理變得更容易還是更困難?React和Angular是如何處理這件事的?在我看來,雙向綁定(以及可變性)使Vue比Reaction更容易理解,但從長遠來看,更不容易擴展。
  • 當用戶成功提交表單時,將觸發onSubmit。在提交時,我們阻止正常的瀏覽器行為(evt.preitDefault()),關閉模態組件(這里是$rens.addBookModal.hid()),觸發addBook方法,并清除表單(initForm())。
  • addBook向/books發送一個POST請求以添加一本新書。
  • 自己查看其余的更改,必要時可以參考Vue文檔。
  • 您能發現客戶端或服務器上的潛在錯誤嗎?自行處理這些以提高用戶體驗。

    最后,更新模板中的“AddBook”按鈕,以便在單擊按鈕時顯示modal:

    <button type="button" class="btn btn-success btn-sm" v-b-modal.book-modal>Add Book</button>

    完整的組件代碼現在應該如下所示:

    <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><div class="btn-group" role="group"><button type="button" class="btn btn-warning btn-sm">Update</button><button type="button" class="btn btn-danger btn-sm">Delete</button></div></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"requiredplaceholder="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"requiredplaceholder="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-group><b-button type="submit" variant="primary">Submit</b-button><b-button type="reset" variant="danger">Reset</b-button></b-button-group></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-lineconsole.error(error);});},addBook(payload) {const path = 'http://localhost:5000/books';axios.post(path, payload).then(() => {this.getBooks();}).catch((error) => {// eslint-disable-next-lineconsole.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>

    試試看!試著增加一本書:

    Alert 組件

    接下來,讓我們添加一個Alert組件,這樣在添加新書后,就可以向用戶顯示一條提示消息。我們將為此單獨創建一個新組件,因為您可能會在許多組件中使用這一功能。

    向“Client/src/Components”添加一個名為Alert.vue的新文件:

    <template><p>It works!</p> </template>

    然后,將其導入Books組件的Script部分,并注冊該組件:

    <script> import axios from 'axios'; import Alert from './Alert.vue';...export default {data() {return {books: [],addBookForm: {title: '',author: '',read: [],},};},components: {alert: Alert,},...}; </script>

    現在,我們可以在template部分引用新組件:

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

    刷新瀏覽器。你現在可以看到:

    有關在其他組件中使用某一組件的更多信息,可以查看官方vue文檔的Composing with Components部分。

    接下來,讓我們將b-alert組件添加到template中::

    <template><div><b-alert variant="success" show>{{ message }}</b-alert><br></div> </template><script> export default {props: ['message'], }; </script>

    注意腳本部分中的props選項。我們可以從父組件(Books)傳遞消息,如下所示:

    <alert message="hi"></alert>

    試試效果:

    查看docs以獲得更多關于props的信息

    要使其具有動態,以便傳遞自定義消息,可以在Books.vue中使用綁定表達式:

    <alert :message="message"></alert>

    將message消息添加到Books.vue中的data選項中:

    data() {return {books: [],addBookForm: {title: '',author: '',read: [],},message: '',}; },

    然后,在addBook中,更新消息:

    addBook(payload) {const path = 'http://localhost:5000/books';axios.post(path, payload).then(() => {this.getBooks();this.message = 'Book added!';}).catch((error) => {// eslint-disable-next-lineconsole.log(error);this.getBooks();}); },

    最后,添加一個v-if,因此只有在showMessage為true的時才會有提示消息:

    <alert :message=message v-if="showMessage"></alert>

    將showMessage添加到data中:

    data() {return {books: [],addBookForm: {title: '',author: '',read: [],},message: '',showMessage: false,}; },

    再次更新addBook,并將showMessage設置為true:

    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-lineconsole.log(error);this.getBooks();}); },

    再試試效果怎么樣!

    挑戰:

  • 思考一下,showMessage什么時候應該設置為false,更新你的代碼。
  • 嘗試使用Alert組件提示錯誤。
  • 將alert重構為可關閉的.
  • PUT路由

    服務器端

    對于更新,我們需要使用唯一的標識符,因為我們不能期待所有標題是唯一的。我們可以使用Python標準庫中的UUID。

    更新server/app.py中的書籍:

    import uuidBOOKS = [{'id': uuid.uuid4().hex,'title': 'On the Road','author': 'Jack Kerouac','read': True},{'id': uuid.uuid4().hex,'title': 'Harry Potter and the Philosopher\'s Stone','author': 'J. K. Rowling','read': False},{'id': uuid.uuid4().hex,'title': 'Green Eggs and Ham','author': 'Dr. Seuss','read': True} ]

    在添加新書時,重構All_Books以添加唯一的id:

    @app.route('/books', methods=['GET', 'POST']) def all_books():response_object = {'status': 'success'}if request.method == 'POST':post_data = request.get_json()BOOKS.append({'id': uuid.uuid4().hex,'title': post_data.get('title'),'author': post_data.get('author'),'read': post_data.get('read')})response_object['message'] = 'Book added!'else:response_object['books'] = BOOKSreturn jsonify(response_object)

    添加一個新的路由處理程序:

    @app.route('/books/<book_id>', methods=['PUT']) def single_book(book_id):response_object = {'status': 'success'}if request.method == 'PUT':post_data = request.get_json()remove_book(book_id)BOOKS.append({'id': uuid.uuid4().hex,'title': post_data.get('title'),'author': post_data.get('author'),'read': post_data.get('read')})response_object['message'] = 'Book updated!'return jsonify(response_object)

    添加輔助方法:

    def remove_book(book_id):for book in BOOKS:if book['id'] == book_id:BOOKS.remove(book)return Truereturn False 花點時間思考一下,您將如何處理id不存在時的情況?如果數據體不正確怎么辦?此外,還可以嘗試著重構輔助方法中的for循環,使其更加Pythonic。

    客戶端

    步驟:

  • 添加模態和表單
  • “更新”按鈕
  • 連接Ajax請求
  • 用戶提示
  • “取消”按鈕
  • (1)增加模態和表單

    首先,在template中添加一個新的modal,寫在第一個modal的下面:

    <b-modal ref="editBookModal"id="book-update-modal"title="Update"hide-footer><b-form @submit="onSubmitUpdate" @reset="onResetUpdate" class="w-100"><b-form-group id="form-title-edit-group"label="Title:"label-for="form-title-edit-input"><b-form-input id="form-title-edit-input"type="text"v-model="editForm.title"requiredplaceholder="Enter title"></b-form-input></b-form-group><b-form-group id="form-author-edit-group"label="Author:"label-for="form-author-edit-input"><b-form-input id="form-author-edit-input"type="text"v-model="editForm.author"requiredplaceholder="Enter author"></b-form-input></b-form-group><b-form-group id="form-read-edit-group"><b-form-checkbox-group v-model="editForm.read" id="form-checks"><b-form-checkbox value="true">Read?</b-form-checkbox></b-form-checkbox-group></b-form-group><b-button-group><b-button type="submit" variant="primary">Update</b-button><b-button type="reset" variant="danger">Cancel</b-button></b-button-group></b-form> </b-modal>

    將表單狀態添加到script部分的data中:

    editForm: {id: '',title: '',author: '',read: [], }, 挑戰:嘗試使用相同的modal來處理POST和PUT請求,而不是使用新的modal。

    (2)“更新”按鈕

    更新表格中的“更新”按鈕功能:

    <buttontype="button"class="btn btn-warning btn-sm"v-b-modal.book-update-modal@click="editBook(book)">Update </button>

    添加一個新方法來更新editForm:

    editBook(book) {this.editForm = book; },

    然后,添加一個方法用來處理表單的提交:

    onSubmitUpdate(evt) {evt.preventDefault();this.$refs.editBookModal.hide();let read = false;if (this.editForm.read[0]) read = true;const payload = {title: this.editForm.title,author: this.editForm.author,read,};this.updateBook(payload, this.editForm.id); },

    (3)連接Ajax請求

    updateBook(payload, bookID) {const path = `http://localhost:5000/books/${bookID}`;axios.put(path, payload).then(() => {this.getBooks();}).catch((error) => {// eslint-disable-next-lineconsole.error(error);this.getBooks();}); },

    (4)用戶提示

    更新updateBook:

    updateBook(payload, bookID) {const path = `http://localhost:5000/books/${bookID}`;axios.put(path, payload).then(() => {this.getBooks();this.message = 'Book updated!';this.showMessage = true;}).catch((error) => {// eslint-disable-next-lineconsole.error(error);this.getBooks();}); },

    (5)“取消”按鈕

    添加方法:

    onResetUpdate(evt) {evt.preventDefault();this.$refs.editBookModal.hide();this.initForm();this.getBooks(); // why? },

    更新initForm:

    initForm() {this.addBookForm.title = '';this.addBookForm.author = '';this.addBookForm.read = [];this.editForm.id = '';this.editForm.title = '';this.editForm.author = '';this.editForm.read = []; },

    在繼續之前,一定要檢查代碼。完成后,測試應用程序。確保在點擊按鈕時modal能夠顯示并且輸入框中填充的值是正確的。

    Delete路由

    服務器端

    更新路由處理程序:

    @app.route('/books/<book_id>', methods=['PUT', 'DELETE']) def single_book(book_id):response_object = {'status': 'success'}if request.method == 'PUT':post_data = request.get_json()remove_book(book_id)BOOKS.append({'id': uuid.uuid4().hex,'title': post_data.get('title'),'author': post_data.get('author'),'read': post_data.get('read')})response_object['message'] = 'Book updated!'if request.method == 'DELETE':remove_book(book_id)response_object['message'] = 'Book removed!'return jsonify(response_object)

    客戶端

    更新“刪除”按鈕如下:

    <buttontype="button"class="btn btn-danger btn-sm"@click="onDeleteBook(book)">Delete </button>

    添加刪除按鈕:

    removeBook(bookID) {const path = `http://localhost:5000/books/${bookID}`;axios.delete(path).then(() => {this.getBooks();this.message = 'Book removed!';this.showMessage = true;}).catch((error) => {// eslint-disable-next-lineconsole.error(error);this.getBooks();}); }, onDeleteBook(book) {this.removeBook(book.id); },

    現在,當用戶單擊“刪除”按鈕時,onDeleteBook方法被觸發,接著觸發removeBook方法。此方法將DELETE請求發送到后端。當響應返回時,顯示提示消息并運行getBooks。

    挑戰:

  • 不要單擊按鈕后直接刪除,而是添加一個確認提示。
  • 當Books中沒有書時,顯示一條信息,比如“沒有書!請加一本”。
  • 結語

    這篇文章涵蓋了使用Vue和Flask設置CRUD應用程序的基礎知識。

    可以檢查一下自己的學習效果,從這篇文章開始回顧,并完成其中的每一個挑戰。

    如果想更多了解,可以查看具體的源碼,源碼地址為flask-vue-crud。

    感謝您的閱讀。

    總結

    以上是生活随笔為你收集整理的[Flask+Vue]Books全栈应用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。