一个简单的 CRUD 操作基本可以看出某个开发框架和平台的特点。Flask 作为一个微框架,在开发一些小型应用的时候非常合适。本文试图从开发一个简单的 Notebook 应用,说明 Flask 开发的基本模式。除 Flask 模块外,本次用到以下插件:
Flask-SQLAlchemy (基于 SQLAlchemy 的扩展,操作数据库)
Flask-WTF (一个表单插件,让 HTML 表单编写更加简单)
Flask 工程文件的结构
Flask 开发并没有业界权威的工程文件结构,但还是有主流的做法。参考网上的文章和代码,我准备采用如下的结构
project-folder/
app /
templates /
static /
__init__.py
controllers.py
models.py
views.py
configs.py
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/', 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/', 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
Flask basic CRUD{{ notes|length }} Notes:
ID | Body | Action |
---|
{% for note in notes %}
{{note.id}} {{note.body}}{% endfor %}
edit_note.html 与 new_note.html 内容相同
New noteNew note
{{ form.body(rows='10',cols='100') }}
{{ form.submit }}
为简化 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)
参考