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/', 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


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flask basic CRUDtitle>
    <link rel="stylesheet" type="text/css" href="../static/styles.css">
head>
<body>
    <a href="{{ url_for('notesbp.new_note') }}">New notea>

    <h4> {{ notes|length }} Notes: h4>

    <table>
        <tr>
            <th>IDth>
            <th>Bodyth>
            <th>Actionth>
        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) }}">Edita>
                    <a href="{{ url_for('notesbp.delete_note', note_id=note.id) }}">Deletea>
                td>
            tr>
        {% endfor %}
    table>
body>
html>

edit_note.htmlnew_note.html 内容相同


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>New notetitle>
head>
<body>
    <h2>New noteh2>

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

本文源代码: https://github.com/stonewm/python-practice-projects/tree/master/Flask/Notebook-v1.0

参考

  • 《Flask Web 开发实战》(李辉)
  • Building a CRUD application with Flask and SQLAlchemy

你可能感兴趣的:(Flask框架)