文章目录
1、简介1.1 flask1.2 vue 2、开发2.1 新建flask项目2.2 安装flask库2.3 新建flask的主脚本2.4 新建Vue项目2.5 安装vue项目依赖项2.6 新增组件Ping.vue2.7 Ping.vue增加HTTP请求2.8 美化vue前端页面2.9 新增组件Books.vue2.10 flask增加路由Books2.11 Books.vue增加HTTP请求2.12 flask修改路由Books2.13 Books.vue增加添加接口2.14 新增组件Alert.vue2.15 修改组件Alert.vue2.16 变量绑定Alert组件2.17 flask端Book对象添加唯一id2.18 Books.vue增加更新操作 结语1、简介
1.1 flask
Flask是一个简单但功能强大的Python微Web框架,非常适合构建RESTful API。像Sinatra(Ruby)和Express(Node)一样,它是最小和灵活的,所以你可以从小处着手,然后根据需要构建更复杂的应用程序。
安装如下:
pip install flask
1.2 vue
Vue 是一个用于构建用户界面的开源 JavaScript 框架。它采用了React和Angular的一些最佳实践。也就是说,与 React 和 Angular 相比,它更平易近人,因此初学者可以快速启动和运行。它也同样强大,因此它提供了创建现代前端应用程序所需的所有功能。
安装如下:
npm create vue@3.6.1
2、开发
2.1 新建flask项目
首先创建一个新的项目目录:
mkdir flask-vue-crudcd flask-vue-crud
创建并激活虚拟环境:
python -m venv myvenv.\myvenv\Scripts\activate
2.2 安装flask库
将 Flask 与 Flask-CORS 扩展一起安装:
pip install Flask Flask-Cors
2.3 新建flask的主脚本
app.py:
# -*- coding:utf-8 -*-from flask import Flask, jsonify, make_responsefrom flask_cors import CORSclass Config(object):DEBUG=TrueJSON_AS_ASCII=False# instantiate the appapp = Flask(__name__)# app.config.from_object(__name__)app.config.from_object(Config)# app.config["JSON_AS_ASCII"] = False# app.config['DEBUG'] = True# app.config['JSONIFY_MIMETYPE'] = "application/json;charset=utf-8"# enable CORSCORS(app, resources={r'/*': {'origins': '*'}})@app.route('/', methods=['GET'])def index():response = make_response(jsonify('爱看书的小沐!'))response.headers['Content-Type'] = 'application/json;charset=utf-8'return response# return jsonify('爱看书的小沐!')# sanity check route@app.route('/ping', methods=['GET'])def ping_pong():return jsonify("xiaomu's pong!")if __name__ == '__main__':app.run(port=5001)
运行上面这个脚本:
flask run --port=5001 --debug
2.4 新建Vue项目
使用强大的create-vue工具,它使用Vite来生成一个定制的项目样板。
在 “flask-vue-crud” 中,运行以下命令来初始化一个新的 Vue 项目:
npm create vue@3.6.1
快速浏览生成的项目结构。这可能看起来很多,但我们只会处理“src”文件夹中的文件和文件夹以及index.html文件。
main.js:app 入口点,它加载并初始化 Vue 以及根组件App.vue:根组件,这是呈现所有其他组件的起点"components":UI 组件的存储位置router/index.js:定义 URL 并将其映射到组件的位置"views":绑定到路由器的 UI 组件的存储位置"assets":静态资产(如图像和字体)的存储位置
查看 client/src/components/HelloWorld.vue 文件。这是一个单个文件组件,分为三个不同的部分:
模板:用于特定于组件的 HTML脚本:组件逻辑通过JavaScript实现样式:用于 CSS 样式
其中index.html 文件是我们 Vue 应用程序的起点:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><link rel="icon" href="/favicon.ico"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vite App</title></head><body><div id="app"></div><script type="module" src="/src/main.js"></script></body></html>
2.5 安装vue项目依赖项
$ cd vue-project$ npm install$ npm run dev
浏览器访问如下:
2.6 新增组件Ping.vue
为了简化操作,请删除“vue-project/src/views”和“vue-project/src/components/icons”文件夹以及vue-project/src/components/TheWelcome.vue和vue-project/src/components/WelcomeItem.vue组件。然后,将一个名为 Ping.vue 的新组件添加到名为 Ping.vue 的“vue-project/src/components”文件夹中:
<template><div><p>{{msg }}</p></div></template><script>export default {name: 'Ping',data() {return {msg: 'Hello!',};},};</script>
更新客户端/src/router/index.js将“/ping”映射到组件,如下所示:
import {createRouter, createWebHistory } from 'vue-router'import Ping from '../components/Ping.vue'const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/ping',name: 'ping',component: Ping},]})export default router
最后,在 vue-project/src/App.vue 中,删除导航以及样式:
<template><RouterView /></template><script setup>import {RouterView } from 'vue-router'</script>
在浏览器访问: http://localhost:5173/ping
2.7 Ping.vue增加HTTP请求
要将客户端 Vue 应用程序与后端 Flask 应用程序连接起来,我们可以使用 Axios 库发送 HTTP 请求。
npm install axios@1.3.6 --save
在 Ping.vue 中更新组件的部分,如下所示:
<template><div><p>{{msg }}</p></div></template><script>import axios from 'axios';export default {name: 'Ping',data() {return {msg: '',};},methods: {getMessage() {const path = 'http://localhost:5001/ping';axios.get(path).then((res) => {this.msg = res.data;}).catch((error) => {console.error(error);});},},created() {this.getMessage();},};</script>
随着 Vue 应用程序在不同的终端窗口中运行,您现在应该在浏览器中看到。
2.8 美化vue前端页面
接下来,让我们将 Bootstrap(一个流行的 CSS 框架)添加到应用程序中,以便我们可以快速添加一些样式。
npm install bootstrap@5.2.3 --save
将引导程序样式导入客户端/src/main.js:
import {createApp } from 'vue'import App from './App.vue'import router from './router'import 'bootstrap/dist/css/bootstrap.css' //add by tomcatimport './assets/main.css'const app = createApp(App)app.use(router)app.mount('#app')
更新vue-project\src\components\Ping.vue的template部分:
<template><div class="container"><button type="button" class="btn btn-primary">{{msg }}</button></div></template>
运行开发服务器:
2.9 新增组件Books.vue
<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>
修改路由文件:vue-project\src\router\index.js
import {createRouter, createWebHistory } from 'vue-router'import Books from '../components/Books.vue'import Ping from '../components/Ping.vue'const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/',name: 'Books',component: Books,},{path: '/ping',name: 'ping',component: Ping},]})export default router
浏览器访问:http://localhost:5173
2.10 flask增加路由Books
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})
浏览器访问如下:
2.11 Books.vue增加HTTP请求
<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:5001/books';axios.get(path).then((res) => {this.books = res.data.books;}).catch((error) => {console.error(error);});},},created() {this.getBooks();},};</script>
浏览器访问如下:
2.12 flask修改路由Books
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:5001/books -d "{\"title\": \"1024\", \"author\": \"Xiao Mu\", \"read\": \"true\"}" -H "Content-Type: application/json"
命令行输入curl指令如下:
flask的web服务端的信息输出如下:
vue前端页面的最下面增加了一行记录如下:
2.13 Books.vue增加添加接口
<template><div class="container"><div class="row"><div class="col-sm-10"><h1>Books</h1><hr><br><br><buttontype="button"class="btn btn-success btn-sm"@click="toggleAddBookModal">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><!-- add new book modal --><divref="addBookModal"class="modal fade":class="{ show: activeAddBookModal, 'd-block': activeAddBookModal }"tabindex="-1"role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><h5 class="modal-title">Add a new book</h5><buttontype="button"class="close"data-dismiss="modal"aria-label="Close"@click="toggleAddBookModal"><span aria-hidden="true">×</span></button></div><div class="modal-body"><form><div class="mb-3"><label for="addBookTitle" class="form-label">Title:</label><inputtype="text"class="form-control"id="addBookTitle"v-model="addBookForm.title"placeholder="Enter title"></div><div class="mb-3"><label for="addBookAuthor" class="form-label">Author:</label><inputtype="text"class="form-control"id="addBookAuthor"v-model="addBookForm.author"placeholder="Enter author"></div><div class="mb-3 form-check"><inputtype="checkbox"class="form-check-input"id="addBookRead"v-model="addBookForm.read"><label class="form-check-label" for="addBookRead">Read?</label></div><div class="btn-group" role="group"><buttontype="button"class="btn btn-primary btn-sm"@click="handleAddSubmit">Submit</button><buttontype="button"class="btn btn-danger btn-sm"@click="handleAddReset">Reset</button></div></form></div></div></div></div><div v-if="activeAddBookModal" class="modal-backdrop fade show"></div></div></template><script>import axios from 'axios';export default {data() {return {activeAddBookModal: false,addBookForm: {title: '',author: '',read: [],},books: [],};},methods: {addBook(payload) {const path = 'http://localhost:5001/books';axios.post(path, payload).then(() => {this.getBooks();}).catch((error) => {console.log(error);this.getBooks();});},getBooks() {const path = 'http://localhost:5001/books';axios.get(path).then((res) => {this.books = res.data.books;}).catch((error) => {console.error(error);});},handleAddReset() {this.initForm();},handleAddSubmit() {this.toggleAddBookModal();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();},initForm() {this.addBookForm.title = '';this.addBookForm.author = '';this.addBookForm.read = [];},toggleAddBookModal() {const body = document.querySelector('body');this.activeAddBookModal = !this.activeAddBookModal;if (this.activeAddBookModal) {body.classList.add('modal-open');} else {body.classList.remove('modal-open');}},},created() {this.getBooks();},};</script>
2.14 新增组件Alert.vue
让我们添加一个组件,以便在添加新书籍后向最终用户显示消息。我们将为此创建一个新组件Alert.vue,因为您可能会在许多组件中使用该功能。
将一个名为 Alert.vue 的新文件添加到 “vue-project/src/components”:
<template><p>It works!爱看书的小沐正在添加Alert</p></template>
然后,将其添加到Books.vue中:
<script>import axios from 'axios';import Alert from './Alert.vue';export default {data() {return {activeAddBookModal: false,addBookForm: {title: '',author: '',read: [],},books: [],};},components: {alert: Alert,},...};</script>
在Books.vue的template部分也添加Alert的使用:
<template><div class="container"><div class="row"><div class="col-sm-10"><h1>Books</h1><hr><br><br><alert></alert><buttontype="button"class="btn btn-success btn-sm"@click="toggleAddBookModal">Add Book</button><br><br>...</div></div>...</div></template>
前端预览如下:
2.15 修改组件Alert.vue
<template><div><div class="alert alert-success" role="alert">{{message }}</div><br/></div></template><script>export default {props: ['message'],};</script>
同时修改Books.vue的template部分中的alert代码:
<alert message="hi, 爱看书的小沐"></alert>
2.16 变量绑定Alert组件
Books.vue 中使用绑定表达式。修改Books.vue的template部分中的alert代码:
<alert :message="message"></alert>
修改Books.vue的script的data()函数:
data() {return {activeAddBookModal: false,addBookForm: {title: '',author: '',read: [],},books: [],message: '',};},
修改Books.vue的script的addBook()函数:
addBook(payload) {const path = 'http://localhost:5001/books';axios.post(path, payload).then(() => {this.getBooks();this.message = 'Book added!';}).catch((error) => {console.log(error);this.getBooks();});},
仅在 true 时显示警报:v-ifshowMessage:
<alert :message=message v-if="showMessage"></alert>
data() {return {activeAddBookModal: false,addBookForm: {title: '',author: '',read: [],},books: [],message: '',showMessage: false,};},
addBook(payload) {const path = 'http://localhost:5001/books';axios.post(path, payload).then(() => {this.getBooks();this.message = 'Book added!';this.showMessage = true;}).catch((error) => {console.log(error);this.getBooks();});},
2.17 flask端Book对象添加唯一id
import uuidBOOKS = [{'id': uuid.uuid4().hex,'title': 'On the Road','author': 'Jack Kerouac','read': True},]@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
2.18 Books.vue增加更新操作
template部分增加:
<!-- edit book modal --><divref="editBookModal"class="modal fade":class="{ show: activeEditBookModal, 'd-block': activeEditBookModal }"tabindex="-1"role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><h5 class="modal-title">Update</h5><buttontype="button"class="close"data-dismiss="modal"aria-label="Close"@click="toggleEditBookModal"><span aria-hidden="true">×</span></button></div><div class="modal-body"><form><div class="mb-3"><label for="editBookTitle" class="form-label">Title:</label><inputtype="text"class="form-control"id="editBookTitle"v-model="editBookForm.title"placeholder="Enter title"></div><div class="mb-3"><label for="editBookAuthor" class="form-label">Author:</label><inputtype="text"class="form-control"id="editBookAuthor"v-model="editBookForm.author"placeholder="Enter author"></div><div class="mb-3 form-check"><inputtype="checkbox"class="form-check-input"id="editBookRead"v-model="editBookForm.read"><label class="form-check-label" for="editBookRead">Read?</label></div><div class="btn-group" role="group"><buttontype="button"class="btn btn-primary btn-sm"@click="handleEditSubmit">Submit</button><buttontype="button"class="btn btn-danger btn-sm"@click="handleEditCancel">Cancel</button></div></form></div></div></div></div><div v-if="activeEditBookModal" class="modal-backdrop fade show"></div>
script部分增加:
activeEditBookModal: false,editBookForm: {id: '',title: '',author: '',read: [],},
template部分的主界面修改:
<buttontype="button"class="btn btn-warning btn-sm"@click="toggleEditBookModal(book)">Update</button>
script部分增加函数:
toggleEditBookModal(book) {if (book) {this.editBookForm = book;}const body = document.querySelector('body');this.activeEditBookModal = !this.activeEditBookModal;if (this.activeEditBookModal) {body.classList.add('modal-open');} else{body.classList.remove('modal-open');}},handleEditSubmit() {this.toggleEditBookModal(null);let read = false;if (this.editBookForm.read) read = true;const payload = {title: this.editBookForm.title,author: this.editBookForm.author,read,};this.updateBook(payload, this.editBookForm.id);},updateBook(payload, bookID) {const path = `http://localhost:5001/books/${bookID}`;axios.put(path, payload).then(() => {this.getBooks();this.message = 'Book updated!';this.showMessage = true;}).catch((error) => {console.error(error);this.getBooks();});},handleEditCancel() {this.toggleEditBookModal(null);this.initForm();this.getBooks(); // why?},
修改函数initForm():
initForm() {this.addBookForm.title = '';this.addBookForm.author = '';this.addBookForm.read = [];this.editBookForm.id = '';this.editBookForm.title = '';this.editBookForm.author = '';this.editBookForm.read = [];},
浏览器预览如下:
结语
如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;
╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地
//(ㄒoㄒ)//,就在评论处留言,作者继续改进;
o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;
(✿◡‿◡)
感谢各位大佬童鞋们的支持!
( ´ ▽´ )ノ ( ´ ▽´)っ!!!