Flask 实现 Rest API

版权声明:本文为博主原创文章,转载请注明出处:http://blog.csdn.net/stone0823 https://blog.csdn.net/stone0823/article/details/82318022

Restful API 已经大行其道,因为我经常需要需要做基于 Restful API 的测试,为了日常测试的方便,用 Flask 来实现 Restful API 接口。没有使用 flask-restful 插件,手动实现。后期再用 flask-restful 插件来改造。通过这种方式,可以更深刻理解插件解决什么问题,也可以看看插件是怎样更优雅地实现的,而不仅仅是用别人的工具。

数据库表的结构

数据放在 MySQL 数据库中中,创建 emp_master 数据表的脚本如下:

create table EMP_MASTER(
	EMP_ID 	        int primary key,
    GENDER	        varchar(10),
	AGE		        int,
    EMAIL	        varchar(50),
    PHONE_NR	    varchar(20),
    EDUCATION	    varchar(20),
    MARITAL_STAT	varchar(20),
    NR_OF_CHILDREN	int
) character set utf8;

示例数据可以从这里获取。

增删改查的存储过程

增删改查用存储过程实现,脚本如下:

获取所有员工主数据的记录

drop procedure if exists getEmployees;

delimiter //

create procedure getEmployees()
begin
 select * from emp_master;
end // 

delimiter ;

新建员工主数据记录

drop procedure if exists createEmployee;

delimiter //

create procedure createEmployee(
	in_empid int, 
    in_gender varchar(10), 
    in_age int,
    in_email varchar(50), 
    in_phonenr varchar(20),
    in_education varchar(20), 
    in_maritalstat varchar(20), 
    in_nrofchildren int
)
begin
	insert into emp_master (EMP_ID, GENDER, AGE, EMAIL, PHONE_NR, EDUCATION, MARITAL_STAT, NR_OF_CHILDREN)
		VALUES (in_empid, in_gender, in_age, in_email, in_phonenr, in_education, in_maritalstat, in_nrofchildren);
end //

delimiter ;

修改员工主数据

drop procedure if exists updateEmployee;

delimiter //

create procedure updateEmployee(
	in_empid int, 
    in_gender varchar(10), 
    in_age int,
    in_email varchar(50), 
    in_phonenr varchar(20),
    in_education varchar(20), 
    in_maritalstat varchar(20), 
    in_nrofchildren int
)
begin
	update emp_master set GENDER = in_gender, AGE = in_age, EMAIL = in_email,
		PHONE_NR = in_phonenr, EDUCATION = in_education,
        MARITAL_STAT = in_maritalstat, NR_OF_CHILDREN = in_nrofchildren
	where EMP_ID = in_empid;
end //

delimiter ;

删除员工主数据

drop procedure if exists deleteEmployee;

delimiter //

create procedure deleteEmployee(
	empid int
)
begin
	delete from emp_master where EMP_ID=empid;
end //

delimiter ;

Flask 工程文档结构

Flask_Restful_MySQL/
    app/
        __init__.py
        config.py
        manage.py
        main/
            __init__.py
            controllers.py
            routes.py

主要的代码在 controllers.pyroutes.py 中。controllers.py 实现对数据库的增删改查,结果以 dict 类型返回。routes.py 处理请求路径和视图函数的映射,对客户端请求返回 json 格式的响应 (Response)。

系统配置

我们先从配置开始。config.py 源代码如下:

# encoding utf-8

class Config(object):
    @staticmethod
    def init_app(app):
        pass

class DBConfig(Config):
    user = 'root'
    password = '123456'
    db = 'stonetest'
    host = 'localhost'

class DevelopmentConfig(Config):
    DEBUG = True

class ProductionConfig(Config):
    DEBUG = False

config = {
    'development': DevelopmentConfig,
    'production': ProductionConfig
}

首先定义一个配置 (Configuration) 的基类 Config, 然后基于 Config 定义三个子类:

  • DBConfig 用于数据库登录的配置;
  • DevelopmentConfig 配置用于开发,此时 DEBUG = True;
  • ProductionConfig 配置用于生产环境,此时 DEBUG = False

创建 app

创建一个名为 app 的 package,也就是创建一个 app 文件夹,文件夹中包含 __init__.py 文件。在 app/__init__.py 中做以下几件事:

  • 创建一个 MySQL 的实例 dbflaskext.mysql 是 Flask 插件,其中的 MySQL 类用于连接 MySQL 数据库并进行 CRUD 操作
  • 创建一个 createApp() 方法,返回 app 的实例,同时,建立 app 与 db 的关联
from flask import Flask
from flaskext.mysql import MySQL
from config import config, DBConfig

db = MySQL()

def createApp(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    # MySQL Configurations
    app.config['MYSQL_DATABASE_HOST'] = DBConfig.host
    app.config['MYSQL_DATABASE_DB'] = DBConfig.db
    app.config['MYSQL_DATABASE_USER'] = DBConfig.user
    app.config['MYSQL_DATABASE_PASSWORD'] = DBConfig.password

    # related db to app
    db.init_app(app)

    return app

manage.py 作为程序入口

manage.py 中输入如下代码:

from app import createApp, db

app = createApp('development')
if __name__ == '__main__':
    app.run()

此时,已经可以从 manage.py 启动该 Flask 工程。但因为没有任何视图函数和路由 (route),启动时浏览器提示如下错误:

路由

app/main/__init__.py 文件输入如下代码:

from flask import Blueprint
main = Blueprint('main', __name__, template_folder='templates')
from . import routes

以上代码创建一个名为 main 的 Blueprint,然后导入 routes 模块。

接下来在 app/__init__.pycreateApp() 方法中添加如下几行代码:

def createApp(config_name):
    ...

    # related db to app
    db.init_app(app)

    # register the blueprint
    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    return app

添加的几行代码如下图:

这几行填加的代码,创建一个名称为 main 的蓝图,并在 app 中进行注册。

然后, 在 app/main/routes.py 文件中,定义一个路由:

from . import main
from .controllers import Employee
from flask import jsonify

@main.route('/')
def inext():
    return 'Hello, this is Flask Restufl Demo.'

再启动 manage.py,已经正确地解析到路径,调用正确的视图函数了:

数据库 CRUD 操作

app/main/controllers.py 中创建对数据库表 emp_master 进行增删改查的四个。代码非常直观,就不再补充说明了:

from flask import request
from .. import db

class Employee(object):

    def listAll(self):
        conn = db.connect()
        curr = conn.cursor()

        curr.callproc('getEmployees')

        items = [dict((curr.description[i][0], value) for i, value in enumerate(row)) for row in curr.fetchall()]

        curr.close()
        conn.close()

        return items

    def create(self):
        payload = request.get_json()
        if payload == None:
            return {
                'desc': 'No payload is provided to create employee.',
                'code': 400
            }

        conn = db.connect();
        curr = conn.cursor()

        params = {
            'EMP_ID': payload['EMP_ID'],
            'GENDER': payload['GENDER'],
            'AGE': payload['AGE'],
            'EMAIL': payload['EMAIL'],
            'EDUCATION': payload['EDUCATION'],
            'PHONE_NR': payload['PHONE_NR'],
            'MARITAL_STAT': payload['MARITAL_STAT'],
            'NR_OF_CHILDREN': payload['NR_OF_CHILDREN']
        }

        # Check for duplication
        curr.execute('SELECT EMP_ID FROM emp_master WHERE EMP_ID=%d' % params['EMP_ID'])
        curr.fetchall()

        if curr.rowcount > 0:
            return {
                'desc': 'Employee already exists.',
                'code': 400
            }

        curr.callproc('createEmployee', (
            params['EMP_ID'],
            params['GENDER'],
            params['AGE'],
            params['EMAIL'],
            params['PHONE_NR'],
            params['EDUCATION'],
            params['MARITAL_STAT'],
            params['NR_OF_CHILDREN']
        ))

        conn.commit()

        return {
            'desc': curr.rowcount,
            'code': 201
        }

    def update(self, empId):
        conn = db.connect()
        curr = conn.cursor()

        payload = request.get_json()
        if payload == None:
            return {
                'desc': 'No payload is provided to update employee.',
                'code': 400
            }

        params = {
            'GENDER': payload['GENDER'],
            'AGE': payload['AGE'],
            'EMAIL': payload['EMAIL'],
            'EDUCATION': payload['EDUCATION'],
            'PHONE_NR': payload['PHONE_NR'],
            'MARITAL_STAT': payload['MARITAL_STAT'],
            'NR_OF_CHILDREN': payload['NR_OF_CHILDREN'],
            'EMP_ID': empId,
        }

        curr.callproc('updateEmployee', (
            params['EMP_ID'],
            params['GENDER'],
            params['AGE'],
            params['EMAIL'],
            params['PHONE_NR'],
            params['EDUCATION'],
            params['MARITAL_STAT'],
            params['NR_OF_CHILDREN']
        ))

        conn.commit()

        if curr.rowcount > 0:
            return {'desc': curr.rowcount, 'code': 200}
        else:
            return {
                'desc': 'Employee %s does not exists.' % empId,
                'code': 400
            }

    def delete(self, empId):
        conn = db.connect()
        curr = conn.cursor()

        curr.callproc('deleteEmployee', [empId])
        conn.commit()

        return {'desc': '%d employee(s) deleted.' % curr.rowcount, 'code': 204}

定义对数据表进行 CRUD 的路由

app/main/routes.py 添加如下代码:

from . import main
from .controllers import Employee
from flask import jsonify

@main.route('/')
def inext():
    return 'Hello, this is Flask Restufl Demo.'

@main.route('/employees')
def listEmployees():
    emp = Employee();
    employees = emp.listAll()
    return jsonify({'rows': employees}), 200;

@main.route('/employees/create', methods=['POST'])
def createEmployee():
    emp = Employee()
    result = emp.create()
    return jsonify({'desc': result['desc']}), result['code']

@main.route('/employees/<empId>', methods=['PUT'])
def updateEmployee(empId):
    emp = Employee()
    result = emp.update(empId)
    return jsonify({'desc': result['desc']}), result['code']

@main.route('/employees/<empId>', methods=['DELETE'])
def deleteEmployee(empId):
    emp = Employee()
    result = emp.delete(empId)
    return jsonify({'desc': result['desc']}), result['code']

这样,一个完整的、可以对数据库进行 CRUD 的 Restful API就全部完成。后续我将再增加 session,cookie 等内容。

猜你喜欢

转载自blog.csdn.net/stone0823/article/details/82318022