Flask 极简 CRUD 操作
一个简单的 CRUD 操作基本可以看出某个开发框架和平台的特点。Flask 作为一个微框架,在开发一些小型应用的时候非常合适。本文试图从开发一个简单的 Notebook 应用,说明 Flask 开发的基本模式。除 Flask 模块外,本次用到以下插件:
- Flask-SQLAlchemy (基于 SQLAlchemy 的扩展,操作数据库)
- Flask-WTF (一个表单插件,让 HTML 表单编写更加简单)
Flask 工程文件的结构
Flask 开发并没有业界权威的工程文件结构,但还是有主流的做法。参考网上的文章和代码,我准备采用如下的结构
project-folder/
app /
templates / <---- 模版文件
static / <---- 静态文件,比如 css 文件
__init__.py <--- 把 app 文件夹作为一个 package,在此文件中创建 flask app
controllers.py <--- 数据库操作
models.py <--- 数据库表与 model 的映射
views.py <--- 视图函数和路由
configs.py <--- app 配置
db_scripts.py <--- 数据库脚本
server.py <--- 服务器端启动入口
配置
我们先从配置开始,因为主要为了说明 Flask CRUD 的要素,所以配置只有最基本的两项,连 SECRET_KEY 都省了。从 Windows 环境变量 获取数据连接的 URI。
# configs.py
import os
SQLALCHEMY_DATABASE_URI = os.getenv('DB_URI')
SQLALCHEMY_TRACK_MODIFICATIONS = False
数据库 CRUD 操作
使用 Flask-SQLAlchemy 进行数据库的 CRUD 操作。首先在 app/models.py
中定义 Model 的结构,映射到数据表和字段:
# models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Notes(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
body = db.Column(db.Text)
def __repr__(self):
return 'Note body: {}'.format(self.body)
然后在 app/controllers.py
文件中,定义数据库的 CRUD 操作的五个方法:
# controllers.py
from app.models import db, Notes
from sqlalchemy import desc
class NotesDao():
def create_note(self, note_body):
new_note = Notes(body=note_body)
db.session.add(new_note)
db.session.commit()
return new_note
def update_note(self, note):
modified_note = Notes.query.get(note.id)
db.session.commit()
return modified_note
def delete_note(self, note):
note_to_delete = Notes.query.get(note.id)
db.session.delete(note_to_delete )
db.session.commit()
return True
def list_all(self):
return Notes.query.order_by(desc(Notes.id)).all()
def get_note(self, id):
return Notes.query.get(id)
定义视图函数
在 app/views.py
文件中,定义蓝图 (blueprint) 和视图函数
from flask import request, render_template, Blueprint, flash, redirect, url_for
from app.controllers import NotesDao
from app.forms import *
# 定义蓝图
notesbp = Blueprint('notesbp', __name__, template_folder='templates')
@notesbp.route('/')
def index():
noteservice = NotesDao()
# return book list to front end
notes = noteservice.list_all()
return render_template('index.html', notes=notes)
@notesbp.route('/new', methods=['GET', 'POST'])
def new_note():
form = NewNoteForm()
if request.method == 'POST':
body = request.form['body']
noteservice = NotesDao()
noteservice.create_note(body)
return redirect(url_for('notesbp.index'))
return render_template('new_note.html', form=form)
@notesbp.route('/edit/<int:note_id>', methods=['GET', 'POST'])
def edit_note(note_id):
form = EditNoteForm()
note = NotesDao().get_note(note_id)
if request.method == 'POST':
body = request.form['body']
note.body = body
NotesDao().update_note(note)
return redirect(url_for('notesbp.index'))
form.body.data = note.body
return render_template('edit_note.html', form=form)
@notesbp.route('/delete/<int:note_id>', methods=['GET'])
def delete_note(note_id):
notesdao = NotesDao()
note = notesdao.get_note(note_id)
notesdao.delete_note(note)
return redirect(url_for('notesbp.index'))
说明:blueprint 是 Flask 代码模块化的一种工具,本例中将当前模块命名为 notesbp
,视图函数利用 blueprint 映射路由, url_for()
函数利用 blueprint 查找视图函数 。
HTML 文件
视图函数中关联的 html 文件,放在 templates 文件夹中。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flask basic CRUD</title>
<link rel="stylesheet" type="text/css" href="../static/styles.css">
</head>
<body>
<a href="{{ url_for('notesbp.new_note') }}">New note</a>
<h4> {{ notes|length }} Notes: </h4>
<table>
<tr>
<th>ID</th>
<th>Body</th>
<th>Action</th>
</tr>
{% for note in notes %}
<tr>
<td> {{note.id}} </td>
<td> {{note.body}} </td>
<td>
<a href="{{ url_for('notesbp.edit_note', note_id=note.id) }}">Edit</a>
<a href="{{ url_for('notesbp.delete_note', note_id=note.id) }}">Delete</a>
</td>
</tr>
{% endfor %}
</table>
</body>
</html>
edit_note.html 与 new_note.html 内容相同
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>New note</title>
</head>
<body>
<h2>New note</h2>
<form method="POST">
{{ form.body(rows='10',cols='100') }}<br/>
{{ form.submit }}
</form>
</body>
</html>
为简化 html 文件的编写,使用了 flask-wtf 表单,表单的代码放在 app/forms.py
文件中:
from wtforms import Form, TextAreaField, SubmitField
class NewNoteForm(Form):
body = TextAreaField('body')
submit = SubmitField('Save')
class EditNoteForm(Form):
body = TextAreaField('body')
submit = SubmitField('Update')
使用工厂函数创建 app
代码放在 app/__init__.py
文件中:
from flask import Flask
import configs
from app.models import db
from app.views import notesbp
def create_app():
app = Flask(__name__)
# 加载配置
app.config.from_object(configs)
# 初始化db
db.app = app
db.init_app(app)
# 注册蓝图
app.register_blueprint(notesbp)
return app
生成数据库表
先用 SQL 语句 create database xxx charset utf8;
创建数据库,然后运行 db_scripts.py
代码创建表:
from app.models import db
from app import create_app
app = create_app()
db.app = app
db.init_app(app)
if __name__ == '__main__':
db.drop_all()
db.create_all()
print ('Done')
启动文件
后端程序通过 server.py
启动,代码如下:
from app import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
参考
- 《Flask Web 开发实战》(李辉)
- Building a CRUD application with Flask and SQLAlchemy
- Build a CRUD Web App With Python and Flask - Part One
本文的代码:请点击Github