用 Flask + Vue 做一个简单的图书管理系统
前言在开始之前,让我们先来聊聊 Django 与 Flask 。用装修风格来类比:一个是轻奢风格(下图左),一个是叙利亚风格(下图右):Django 的使用更加惬意,大概就是租客口中的 ‘拎包入住’ 的模式,工具齐全,只需要考虑吃 肉夹馍(CBV)还是 4菜1汤(FBV),偶尔要换个冰箱(数据库)还得考虑合不合适。而使用 Flask 就如同找了一个叙利亚风格的出租房,看上去很简陋,欸,实际上又什么
效果图:
前言
在开始之前,让我们先来聊聊 Django 与 Flask 。用装修风格来类比:一个是轻奢风格(下图左),一个是叙利亚风格(下图右):
Django 的使用更加惬意,大概就是租客口中的 ‘拎包入住’ 的模式,工具齐全,只需要考虑吃 肉夹馍(CBV)还是 4菜1汤(FBV),偶尔要换个冰箱(数据库)还得考虑合不合适。
而使用 Flask 就如同找了一个叙利亚风格的出租房,看上去很简陋,欸,实际上又什么都有(指 地板、天花板 ),不过想要别有一番风味,还得自己的喜好来布置。
#Ps:文章最后附上了项目全部的代码
1、铺地板(flask 基础样式编写)
编码工具各凭喜好,我这里使用的 Visio Studio Code ,由于要使用到 vue框架 ,我们要在一开始就把 Flask 跟 vue 区分开来,给他们安排上单人包间。也就是说新建两个文件夹,前后 端代码分开存放。
在后端文件夹中新建一个 app.py 来作为我们后端的主程序,在里面写入:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
大概就是这样子:
接着我们打开控制台,进入到 flask 文件夹中,在里面输入以下代码:
set FLASK_APP=app.py
# 设置 FLASK 环境变量,如果名称是 app.py 或者 wsgi.py 可以忽略这一句代码
flask run
# 启动
可以看到控制台输出了一个IP,在浏览器中打开:
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
2、选择一个储物室(数据库)
这里我选用的是 Flask + SQLAlchemy + SQLite 作为我们的数据存储体系,在 app 中导入 SQLAlchemy:
from flask_sqlalchemy import SQLAlchemy
根据我们需要的字段,创建表:
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.sqlite'
# 指定数据库文件
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# 允许修改跟踪数据库
db = SQLAlchemy(app)
class Books(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80), nullable=False)
author = db.Column(db.String(120), nullable=False)
read= db.Column(db.Boolean)
db.create_all()
# 创建表
重新运行 app.py 文件,可以看到数据库文件已经生成成,接下来我们定义几个函数便于我们对数据库的增删改查:
# 增
def insertData(title,author,read):
book=Books(title=title,author=author,read=read)
db.session.add_all([book])
db.session.commit()
# 查
def selectDataAll():
book_list=[]
books = Books.query.all()
# 类似于 select * from Books
for s in books:
dic = {}
dic['id'] = s.id
dic['title'] = s.title
dic['author'] = s.author
dic['read'] = s.read
print(f'Id: {s.id} | Title: {s.title} | Author: {s.author} | Read: {s.read}')
print('----')
book_list.append(dic)
return book_list
# 删
def deleteData(id):
book = Books.query.get(id)
# 类似于 select * from Books where id = id
db.session.delete(book)
db.session.commit()
# 提交操作到数据库
# 改
def updateData(id, title='', author='', read='', new_id=''):
book = Books.query.get(id)
if not title=='':
book.title = title
if not author == '':
book.author = author
if not read == '':
book.read = read
if not new_id == '':
book.id=new_id
db.session.commit()
利用我们写好的 增函数 添加数据:
···
insertData('book1','zhangsan',0)
insertData('book2','lisi',1)
···
运行 app.py 后数据就被添加到数据库中。
#Ps:运行后记得在 app.py 中删除上面两句代码
3、提交数据库数据至前端
···
from flask import Flask, jsonify, request, make_response
···
···
@app.route('/',methods=['GET','POST'])
def all_books():
response_object = {'status':'success'}
books = selectDataAll()
response_object['books']=books
return jsonify(response_object)
···
一切就绪,我们运行 app.py 就可以在网页中看到数据库中的数据已经被显示出来了:
4、vue框架的使用
首先按照下方流程配置所需的应用与环境:
1、我们的电脑中需要 node.js (中文网地址 : http://nodejs.cn/),直接下载安装就OK了( 安装教程 : https://www.runoob.com/nodejs/nodejs-install-setup.html )。
2、安装cnpm( cmd 输入:npm install -g cnpm --registry=http://registry.npm.taobao.org)
3、安装 vue 脚手架 ( cmd 输入:npm install -g vue-cli )
4、安装 webpack( cmd 输入:npm install webpack -g )
5、找到我们先前创建的vue文件夹,cd 进去,cmd 输入 : vue create 文件名 ,接着会出现一个界面:
第一个选项是 过去保存的配置
第二个是默认配置
第三个是自定义(手动选择要素)
我们选择自定义配置 ,小键盘 上下箭头移动,空格是 选中/反选 。
这里我们全选。
然后按回车,进入下一个阶段:
#Ps:注意:最后一条的意思是 :以后的项目是否使用以上配置 (保存配置)
安装完后,我们进入到 vue 根目录下 cmd 中输入 npm run serve
#Ps:如果失败 请检查根目录下的 package.json 文件内容,以及尝试使用 npm run dev 指令启动
启动成功如下图:
5、跨域问题处理以及代码配置
到了这一步就差不多接近尾声了,我们通过观察效果图可以得出我们需要的工具有 :
vue:
- Bootstrap # 自带样式,使用便捷 ( vue 根目录下 cmd 输入:npm install bootstrap-vue --save)
- axios # 用于解决跨域问题 ( vue 根目录下 cmd 输入:npm install axios )
Flask:
- CORS # 用于解决跨域问题 ( cmd 输入:pip3 install flask_cors)
前端安装 axios 后,以 axios.post 这样的方式来处理跨域交互
将以上工具安装完毕后,我们开始最后的工作。
1、修改 flask 文件夹中的 app.py(配置跨域交互) :
···
from flask_cors import CORS # 导入CORS 包
···
···
@app.after_request
def after(resp):
resp = make_response(resp)
resp.headers['Access-Control-Allow-Origin'] = '*' # 允许跨域地址
resp.headers['Access-Control-Allow-Methods'] = '*' # 请求 ‘*’ 就是全部
resp.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type' # 头部
resp.headers['Access-Control-Allow-Credentials'] = 'True'
return resp
CORS(app, resources=r'/*', supports_credentials=True)
···
2、在vue文件夹下的 src / components 中创建 Alert.vue 文件 以及 Books.vue 文件
3、配置 router / index.js(或者是 index.ts)
import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";
import Books from "../components/Books.vue";
import Home from "../components/HelloWorld.vue";
import 'bootstrap/dist/css/bootstrap.css';
import BootstrapVue from 'bootstrap-vue'
Vue.config.productionTip=false
Vue.use(BootstrapVue);
Vue.use(VueRouter);
const routes: Array<RouteConfig> = [
{
path: "/home",
name: "Home",
component: Home,
},
{
path: "/", # 将books.vue添加到路由中去
name: "Books",
component: Books,
},
{
path: "/about",
name: "About",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue"),
},
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
export default router;
4、配置 Books.vue 以及 Alert.vue
# Books.vue
<template>
<div class="container" align="left">
<div class="row">
<div class="col-sm-10">
<h1>Books</h1>
<hr />
<br />
<div>
<b-alert variant="success" show>{{ message }}</b-alert>
<br>
</div>
<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">Update</button>
<button type="button" class="btn btn-danger">Delete</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script>
import axios from "axios";
import Alert from './Alert.vue'
export default {
data() {
return {
props: ['message'],
books: [],
BookForm: {
delbook:"",
id: "",
title: "",
author: "",
read: []
},
message: '',
};
},
commponents:{
alert:Alert,
},
methods: {
getBooks() {
const path="http://127.0.0.1:5000";
axios
.get(path)
.then(res => {
this.books=res.data.books;
})
.catch(error => {
console.error(error);
});
},
initForm() {
this.BookForm.delbook='null';
this.BookForm.id="null";
this.BookForm.title="";
this.BookForm.author="";
this.BookForm.read=[];
},
},
created() {
this.getBooks();
}
};
</script>
# Alert.vue
<template><div></div></template>
在books.vue 中,我们主要是搭建了网页的基础框架,目前只能获取数据库的数据并显示:
接着,我们开始编写 Add Book 按钮的 代码:
<template>
<div class="container" align="left">
<div class="row">
<div class="col-sm-10">
<h1>Books</h1>
<hr />
<br />
<div>
<b-alert variant="success" show>{{ message }}</b-alert>
<br>
</div>
<br />
<button type="button" class="btn btn-success btn-sm" v-b-modal.book-add>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">Update</button>
<button type="button" class="btn btn-danger">Delete</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- --------------------------------Add------------------------------ -->
<!-- 注意,与上面的 Add Book 按钮绑定 -->
<b-modal ref="addBookModal" id="book-add" title="Add a new book" hide-footer>
<b-form @submit="add_onSubmit" @reset="add_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="BookForm.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="BookForm.author"
required
placeholder="Enter author"
></b-form-input>
</b-form-group>
<b-form-group id="form-read-group">
<b-form-checkbox-group v-model="BookForm.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";
import Alert from './Alert.vue'
export default {
data() {
return {
props: ['message'],
books: [],
BookForm: {
delbook:"",
id: "",
title: "",
author: "",
read: []
},
message: '',
};
},
commponents:{
alert:Alert,
},
methods: {
getBooks() {
const path="http://127.0.0.1:5000";
axios
.get(path)
.then(res => {
this.books=res.data.books;
})
.catch(error => {
console.error(error);
});
},
initForm() {
this.BookForm.delbook='null';
this.BookForm.id="null";
this.BookForm.title="";
this.BookForm.author="";
this.BookForm.read=[];
},
// --------------------------Add-----------------------------
addBook(payload) {
const path="http://127.0.0.1:5000";
axios
.post(path, payload)
.then(() => {
this.getBooks();
this.message = 'Book added!'
})
.catch(error => {
console.log(error);
this.getBooks();
});
},
add_onSubmit(evt) {
evt.preventDefault();
this.$refs.addBookModal.hide();
let read=false;
if(this.BookForm.read[0]) read=true;
const payload={
del_id:"null",
id:"null",
title: this.BookForm.title,
author: this.BookForm.author,
read
};
this.addBook(payload);
this.initForm();
},
add_onReset(evt) {
evt.preventDefault();
this.$refs.addBookModal.hide();
this.initForm();
},
},
created() {
this.getBooks();
}
};
</script>
修改后端代码(app.py)增加对 POST 请求的 辨别与操作:
···
@app.route('/',methods=['GET','POST','OPTIONS'])
def all_books():
response_object = {'status':'success'}
books = selectDataAll()
if request.method == 'POST':
post_data = request.get_json()
print(post_data)
books_id = post_data.get('id')
del_id = post_data.get('del_id')
title = post_data.get('title'),
author = post_data.get('author'),
read = post_data.get('read')
insertData(
title=title[0],
author=author[0],
read=read
)
response_object['message'] = 'Book added!'
else:
response_object['books']=books
return jsonify(response_object)
···
如法炮制,编写 update 按钮点击事件以及 del 事件
附 Books.vue 全部代码:
<template>
<div class="container" align="left">
<div class="row">
<div class="col-sm-10">
<h1>Books</h1>
<hr />
<br />
<div>
<b-alert variant="success" show>{{ message }}</b-alert>
<br>
</div>
<br />
<button type="button" class="btn btn-success btn-sm" v-b-modal.book-add>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" v-b-modal.book-up @click="book_update(index)">Update</button>
<button type="button" class="btn btn-danger" @click="book_del(index)">Delete</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ------------------------------------------------------Add-------------------------------------------------- -->
<b-modal ref="addBookModal" id="book-add" title="Add a new book" hide-footer>
<b-form @submit="add_onSubmit" @reset="add_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="BookForm.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="BookForm.author"
required
placeholder="Enter author"
></b-form-input>
</b-form-group>
<b-form-group id="form-read-group">
<b-form-checkbox-group v-model="BookForm.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>
<!-- ----------------------------------------------------Up date------------------------------------------------ -->
<b-modal ref="upBookModal" id="book-up" title="update book" hide-footer>
<b-form @submit="up_onSubmit" @reset="up_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="BookForm.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="BookForm.author"
required
placeholder="Enter author"
></b-form-input>
</b-form-group>
<b-form-group id="form-read-group">
<b-form-checkbox-group v-model="BookForm.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>
<!-- ------------------------------------------------------del-------------------------------------------------- -->
</div>
</template>
<script>
import axios from "axios";
import Alert from './Alert.vue'
export default {
data() {
return {
props: ['message'],
books: [],
BookForm: {
delbook:"",
id: "",
title: "",
author: "",
read: []
},
message: '',
};
},
commponents:{
alert:Alert,
},
methods: {
getBooks() {
const path="http://127.0.0.1:5000";
axios
.get(path)
.then(res => {
this.books=res.data.books;
})
.catch(error => {
console.error(error);
});
},
initForm() {
this.BookForm.delbook='null';
this.BookForm.id="null";
this.BookForm.title="";
this.BookForm.author="";
this.BookForm.read=[];
},
// --------------------------Add-----------------------------
addBook(payload) {
const path="http://127.0.0.1:5000";
axios
.post(path, payload)
.then(() => {
this.getBooks();
this.message = 'Book added!'
})
.catch(error => {
console.log(error);
this.getBooks();
});
},
add_onSubmit(evt) {
evt.preventDefault();
this.$refs.addBookModal.hide();
let read=false;
if(this.BookForm.read[0]) read=true;
const payload={
del_id:"null",
id:"null",
title: this.BookForm.title,
author: this.BookForm.author,
read
};
this.addBook(payload);
this.initForm();
},
add_onReset(evt) {
evt.preventDefault();
this.$refs.addBookModal.hide();
this.initForm();
},
// ------------------------up date-----------------------
book_update:function(index){
this.BookForm.id=index+1
},
upBook(payload) {
const path="http://127.0.0.1:5000";
axios
.post(path, payload)
.then(() => {
this.getBooks();
this.message = 'Book update!'
})
.catch(error => {
console.log(error);
this.getBooks();
});
},
up_onSubmit(evt) {
evt.preventDefault();
this.$refs.upBookModal.hide();
let read=false;
if(this.BookForm.read[0]) read=true;
const payload={
id: this.BookForm.id,
del_id: "null",
title: this.BookForm.title,
author: this.BookForm.author,
read
};
this.upBook(payload);
this.initForm();
},
up_onReset(evt) {
evt.preventDefault();
this.$refs.upBookModal.hide();
this.initForm();
},
// --------------------------del-------------------------
book_del:function(index){
this.BookForm.delbook=index+1
let read=false;
if(this.BookForm.read[0]) read=true;
const payload={
id:"null",
del_id: this.BookForm.delbook,
title: this.BookForm.title,
author: this.BookForm.author,
read
};
this.delBook(payload);
this.initForm();
},
delBook(payload) {
const path="http://127.0.0.1:5000";
axios
.post(path, payload)
.then(() => {
this.getBooks();
this.message = 'Book del!'
})
.catch(error => {
console.log(error);
this.getBooks();
});
},
},
created() {
this.getBooks();
}
};
</script>
附后端代码:
from flask import Flask, jsonify, request, make_response
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
app = Flask(__name__)
@app.after_request
def after(resp):
resp = make_response(resp)
resp.headers['Access-Control-Allow-Origin'] = '*'
resp.headers['Access-Control-Allow-Methods'] = '*'
resp.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type'
resp.headers['Access-Control-Allow-Credentials'] = 'True'
return resp
app.config['DEBUG'] = True
CORS(app, resources=r'/*', supports_credentials=True)
# ------------------database----------------------------
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.sqlite'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
class Books(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80), nullable=False)
author = db.Column(db.String(120), nullable=False)
read= db.Column(db.Boolean)
db.create_all()
# 增
def insertData(title,author,read):
book=Books(title=title,author=author,read=read)
db.session.add_all([book])
db.session.commit()
# 查
def selectDataAll():
book_list=[]
books = Books.query.all()
for s in books:
dic = {}
dic['id'] = s.id
dic['title'] = s.title
dic['author'] = s.author
dic['read'] = s.read
print(f'Id: {s.id} | Title: {s.title} | Author: {s.author} | Read: {s.read}')
print('----')
book_list.append(dic)
return book_list
# 删
def deleteData(id):
book = Books.query.get(id)
db.session.delete(book)
db.session.commit()
# 改
def updateData(id, title='', author='', read='', new_id=''):
book = Books.query.get(id)
if not title=='':
book.title = title
if not author == '':
book.author = author
if not read == '':
book.read = read
if not new_id == '':
book.id=new_id
db.session.commit()
# ------------------database---end----------------------
@app.route('/',methods=['GET','POST','OPTIONS'])
def all_books():
response_object = {'status':'success'}
books = selectDataAll()
if request.method == 'POST':
post_data = request.get_json()
print(post_data)
books_id = post_data.get('id')
del_id = post_data.get('del_id')
if books_id == 'null' and del_id == 'null':
title = post_data.get('title'),
author = post_data.get('author'),
read = post_data.get('read')
insertData(
title=title[0],
author=author[0],
read=read
)
response_object['message'] = 'Book added!'
elif (not books_id == 'null') and del_id == 'null':
put_data = post_data
books_id = put_data.get('id')
title = put_data.get('title')
author = put_data.get('author')
read = put_data.get('read')
updateData(
id=books_id,
title=title,
author=author,
read=read
)
response_object['message'] = 'Book update!'
else:
deleteData(del_id)
response_object['message'] = 'Book del!'
else:
response_object['books']=books
books = selectDataAll()
for i in range(len(books)):
id=books[i]['id']
updateData(id=id,new_id=i+1)
print(response_object)
return jsonify(response_object)
if __name__ == '__main__':
app.run(port=5000,debug=True)
至此结束,码字辛苦,还请各位哥哥姐姐留个赞再走呗,先谢谢了嗷~!
更多推荐
所有评论(0)