1,前言
Python的强大,已经涉及到软件开发领域的方方面面。然而,Python入门容易,精确很难,需要深入研究。
在Web方面同样如此,常用的Python Web框架,例如Django、Flask、Tornado等等,共计有100多种,各有优劣。本文以Flask为例,介绍Flask的Restful实现方式,主要实现对数据表的增删查改操作。
2,需求
在开发代码之前,需要提前明确URL请求、HTTP方法与对应的动作。
2.1,Mysql数据表
创建一个简单的数据表,表名是student,存储学生的name、age、birth_time信息。
2.2,学生列表的操作
URL:http://127.0.0.1:5002/student/
- get 获取学生列表
- post 创建新学生信息
2.3,单个学生的操作
URL:http://127.0.0.1:5002/student/
- get 获取单个学生信息
- put 更新单个学生信息
- delete 删除单个学生信息
2.4,附属功能
- 要求每次请求之前,记录用户的IP、请求参数等信息,便于统计分析
- 要求每次数据库操作,判断数据库连接是否正常,避免长时间未进行DB操作而造成DB连接失连
3,Flask项目
3.1,创建Flask项目
- Windows可以使用PyCharm安装,或者手动安装
- Ubuntu安装flask,执行 sudo apt-get install python-flask
- 创建hello.py,测试flask是否OK
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
- 启动flask,执行 python hello.py
- 测试flask,执行 curl http://127.0.0.1:5000/,发现返回“Hello World!”,证明安装成功
- 如果返回缺少某些python包,莫急,逐个安装即可
3.2,目录规划
按照MVC的思想,对控制层和视图层进行分离。控制层主要是对数据库的处理,可以按照不同的数据表进行分文件处理;视图层,按照不同的功能模块,进行份文件处理。这样,便于项目维护和模块扩展。
不过,一定还有更好的目录规划方式,因项目或团队不同而不同,适合自己的即是最好的。
下面是初步划分的文件目录,可以参考。
D:\PYTEST\FLASK_TEST
│ ctrl_student.py # 视图层,响应URL请求
│ db_student.py # 控制层,数据库处理
│ flask_test.py # Flask主文件,控制URL路由
│ test.py # 测试文件
├─static
└─templates
4,源码
4.1,数据库表
- student表结构
CREATE TABLE `student` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`birth_time` datetime DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`is_del` int(10) unsigned zerofill DEFAULT '0000000000',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;
4.2,Python源码
- flask_test.py
# coding:utf8
from flask import Flask, render_template, request
from flask_restful import Api
from ctrl_student import StudentList, Student
app = Flask(__name__)
api = Api(app)
api.add_resource(StudentList, '/student/')
api.add_resource(Student, '/student/')
@app.before_request
def before_request():
ip = request.remote_addr
url = request.url
form = request.form # 请求的数据,可执行searchword = request.form.get('key', '') ?????????测试(带参数的post请求)过程中form为空,不清楚原因
args = request.args # ?key=value,可执行searchword = request.args.get('key', '')
values = request.values # form和args的元组
headers = request.headers
method = request.method
path = request.path
base_url = request.base_url
url_root = request.url_root
print "ip", ip
print "url", url
# print "form", form
# print "args", args
# print "values", values
# print "headers", headers
# print "method", method
# print "path", path
# print "base_url", base_url
# print "url_root", url_root
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5002)
- ctrl_student.py
# coding:utf8
from flask_restful import reqparse, abort, Api, Resource
from db_student import DbStudent
# 404操作
def abort_if_todo_doesnt_exist(student_id):
if DbStudent.get_student_by_id(student_id) == {}:
abort(404, message="Todo {} doesn't exist".format(student_id))
# 入参解析器
parser = reqparse.RequestParser()
# parser.add_argument('id')
parser.add_argument('name')
parser.add_argument('age')
parser.add_argument('birth_time')
# 学生信息操作
class Student(Resource):
def __init__(self):
pass
# 获取student信息
def get(self, student_id):
abort_if_todo_doesnt_exist(student_id)
res = DbStudent.get_student_by_id(student_id)
return res, 200
# 更新student信息
def put(self, student_id):
print "put come in"
abort_if_todo_doesnt_exist(student_id)
args = parser.parse_args()
print args
DbStudent.update_student_by_id(student_id, args)
return {"status":"success"}, 200
# 删除student信息
def delete(self, student_id):
print "put come in"
abort_if_todo_doesnt_exist(student_id)
DbStudent.delete_student_by_id(student_id)
return {"status":"success"}, 200
# 兼容post操作,获取student信息
def post(self, student_id):
print "post come in"
return self.get(student_id)
# 学生列表操作
class StudentList(Resource):
# 获取学生信息列表
def get(self):
res = DbStudent.get_student_list()
return res, 200
# 增加单个学生信息
def post(self):
args = parser.parse_args()
DbStudent.insert_student(args)
return {"status":"success"}, 200
- db_student.py
# coding:utf8
import MySQLdb
host = "192.168.1.64"
port = 3306
user = "******"
passwd = "******"
db = "test"
conn = None
cur = None
# 执行数据库操作之前判断数据库连接是否OK,如果异常则创建一次连接
def db_opt(func):
def db_ping():
global conn, cur
try:
cur.execute("select 1")
cur.fetchone()
except:
conn = MySQLdb.connect(host=host, port=port, user=user, passwd=passwd, db=db, charset="utf8")
cur = conn.cursor()
print "build a new connection"
def wrapper(*args, **kwargs):
global conn, cur
try:
db_ping()
res = func(*args, **kwargs)
print "db_opt", func, args, res
return res
except:
traceback.print_exc('wrapper ')
return {'status':False, 'data':'db operation maybe have some errors.'}
return wrapper
class DbStudent:
# 根据student_id获取学生信息
@staticmethod
@db_opt
def get_student_by_id(student_id):
sql = 'select id, name, age, birth_time from student where id = %s and is_del = 0' % student_id
cur.execute(sql)
res = cur.fetchall()
if len(res) == 0:
return {}
else:
(id, name, age, birth_time) = res[0]
dict_student ={}
dict_student['id'] = id
dict_student['name'] = name
dict_student['age'] = age
dict_student['birth_time'] = str(birth_time)
return dict_student
# 根据name获取学生信息
@staticmethod
@db_opt
def get_student_by_name(name):
sql = 'select id, name, age, birth_time from student where name = "%s" and is_del = 0' % name
cur.execute(sql)
res = cur.fetchall()
if len(res) == 0:
return {}
else:
(id, name, age, birth_time) = res[0]
dict_student = {}
dict_student['id'] = id
dict_student['name'] = name
dict_student['age'] = age
dict_student['birth_time'] = str(birth_time)
return dict_student
# 根据student_id更新学生信息
@staticmethod
@db_opt
def update_student_by_id(student_id, student_info):
sql = 'update student set name = "%s", age = %s, birth_time = "%s" where id = %s' % \
(student_info['name'], student_info['age'], student_info['birth_time'], student_id)
cur.execute(sql)
conn.commit
# 增加一条学生信息
@staticmethod
@db_opt
def insert_student(student_info):
sql = 'insert into student(name, age, birth_time, create_time) values("%s", %s, "%s", now())' % \
(student_info['name'], student_info['age'], student_info['birth_time'])
cur.execute(sql)
conn.commit
# 根据student_id删除学生信息(更新is_del字段)
@staticmethod
@db_opt
def delete_student_by_id(student_id):
sql = "update student set is_del = 1 where id = %s" % student_id
cur.execute(sql)
conn.commit
# 获取学生信息列表
@staticmethod
@db_opt
def get_student_list():
sql = 'select id, name, age, birth_time from student where is_del = 0'
cur.execute(sql)
res = cur.fetchall()
if len(res) == 0:
return {}
else:
student_list = []
for (id, name, age, birth_time) in res:
dict_student = {}
dict_student['id'] = id
dict_student['name'] = name
dict_student['age'] = age
dict_student['birth_time'] = str(birth_time)
student_list.append( dict_student )
return {"student_list": student_list}
if __name__ == '__main__':
print DbStudent.get_student_by_id(1)
print DbStudent.get_student_by_name('zhangsp')
print DbStudent.get_student_list()
5,测试
Web地址:http://192.168.1.64:5002/student/
获取学生信息列表
curl -X GET http://192.168.1.64:5002/student/
- 增加1个学生信息
curl -H "Content-type: application/json" -X POST -d '{"age": 3, "birth_time": "2017-08-02 18:15:42", "name": "c"}' http://192.168.1.64:5002/student/
- 更新单个学生信息
curl -H "Content-type: application/json" -X PUT -d '{"age": 333, "birth_time": "2017-08-02 18:15:42", "name": "c"}' http://192.168.1.64:5002/student/19
- 删除单个学生信息
curl -H "Content-type: application/json" -X DELETE http://192.168.1.64:5002/student/19
6,参考资料
- 关于Flask的request属性
http://blog.csdn.net/yannanxiu/article/details/53116652 - python-flask之request的属性
http://www.cnblogs.com/wangjikun/p/6935592.html - Flask使用小结
http://python.jobbole.com/84003/ - Linux curl命令详解
http://www.cnblogs.com/duhuo/p/5695256.html