Skip to content

Commit 1cdf0f4

Browse files
committed
update python_restful_api.py
1 parent a9dff55 commit 1cdf0f4

File tree

2 files changed

+151
-39
lines changed

2 files changed

+151
-39
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838

3939
### python_restful_api.py: 利用Python和Flask快速开发RESTful API
4040

41+
### python_restful_api.py: RESTful API进阶: 连接数据库、添加参数、Token认证、返回代码说明等
42+
4143
### python_context.py: With语句和上下文管理器ContextManager
4244
===================================================================================================
4345

python_restful_api.py

Lines changed: 149 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,72 +4,182 @@
44
python_restful_api.py by xianhu
55
"""
66

7-
from flask import Flask
8-
from flask_restful import reqparse, abort, Api, Resource
7+
import sqlalchemy
8+
import sqlalchemy.orm
9+
import sqlalchemy.ext.declarative
10+
from flask import Flask, g
11+
from flask_restful import reqparse, Api, Resource
12+
from flask_httpauth import HTTPTokenAuth
913

14+
15+
# Flask相关变量声明
1016
app = Flask(__name__)
1117
api = Api(app)
1218

13-
ITEMS = {
14-
'item1': {'name': 'Allen', 'age': 19},
15-
'item2': {'name': 'Lily', 'age': 18},
16-
'item3': {'name': 'James', 'age': 20},
19+
# 认证相关
20+
auth = HTTPTokenAuth(scheme="token")
21+
TOKENS = {
22+
"fejiasdfhu",
23+
"fejiuufjeh"
1724
}
1825

1926

20-
def abort_if_item_doesnt_exist(item_id):
21-
if item_id not in ITEMS:
22-
abort(404, message="Item {} doesn't exist".format(item_id))
27+
@auth.verify_token
28+
def verify_token(token):
29+
if token in TOKENS:
30+
g.current_user = token
31+
return True
32+
return False
2333

2434

25-
def get_new_item_id():
26-
for key in ITEMS:
27-
item_id = 'item' + str(int(key.strip('item')) + 1)
28-
if item_id not in ITEMS:
29-
return item_id
35+
# 数据库相关变量声明
36+
engine = sqlalchemy.create_engine("mysql+pymysql://username:password@ip/db_name", encoding="utf8", echo=False)
37+
BaseModel = sqlalchemy.ext.declarative.declarative_base()
3038

3139

32-
parser = reqparse.RequestParser()
33-
parser.add_argument('name', type=str, required=True, help='need name data')
34-
parser.add_argument('age', type=int, required=True, help='need age data')
40+
# 构建数据模型User
41+
class User(BaseModel):
42+
__tablename__ = "Users"
43+
__table_args__ = {
44+
"mysql_engine": "InnoDB",
45+
"mysql_charset": "utf8",
46+
}
3547

48+
# 表结构,具体更多的数据类型自行百度
49+
id = sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True, autoincrement=True)
50+
name = sqlalchemy.Column("name", sqlalchemy.String(50), nullable=False)
51+
age = sqlalchemy.Column("age", sqlalchemy.Integer, nullable=False)
3652

37-
# 操作(put / get / delete)单一资源
38-
class Todo(Resource):
3953

40-
def put(self, item_id):
41-
args = parser.parse_args()
42-
item = {'name': args['name'], 'age': args['age']}
43-
ITEMS[item_id] = item
44-
return item, 201
54+
# 构建数据模型的json格式
55+
def get_json(user):
56+
return {"id": user.id, "name": user.name, "age": user.age}
57+
58+
59+
# 利用Session对象连接数据库
60+
DBSessinon = sqlalchemy.orm.sessionmaker(bind=engine)
61+
session = DBSessinon()
62+
BaseModel.metadata.drop_all(engine)
63+
BaseModel.metadata.create_all(engine)
64+
65+
# RESTfulAPI的参数解析 -- put / post参数解析
66+
parser_put = reqparse.RequestParser()
67+
parser_put.add_argument("name", type=str, required=True, help="need name data")
68+
parser_put.add_argument("age", type=int, required=True, help="need age data")
69+
70+
# RESTfulAPI的参数解析 -- get参数解析
71+
parser_get = reqparse.RequestParser()
72+
parser_get.add_argument("limit", type=int, required=False)
73+
parser_get.add_argument("offset", type=int, required=False)
74+
parser_get.add_argument("sortby", type=str, required=False)
4575

46-
def get(self, item_id):
47-
abort_if_item_doesnt_exist(item_id)
48-
return ITEMS[item_id], 200
4976

50-
def delete(self, item_id):
51-
abort_if_item_doesnt_exist(item_id)
52-
del ITEMS[item_id]
53-
return '', 204
77+
# 操作(put / get / delete)单一资源
78+
class Todo(Resource):
79+
# 添加认证
80+
decorators = [auth.login_required]
81+
82+
def put(self, user_id):
83+
"""
84+
更新用户数据: curl http://127.0.0.1:5000/users/1 -X PUT -d "name=Allen&age=20" -H "Authorization: token fejiasdfhu"
85+
"""
86+
args = parser_put.parse_args()
87+
user_ids_set = set([user.id for user in session.query(User.id)])
88+
print(user_ids_set)
89+
90+
# 用户不存在,返回404
91+
if user_id not in user_ids_set:
92+
return None, 404
93+
94+
# 更新用户数据
95+
user = session.query(User).filter(User.id == user_id)[0]
96+
user.name = args["name"]
97+
user.age = args["age"]
98+
session.merge(user)
99+
session.commit()
100+
101+
# 更新成功,返回201
102+
return get_json(user), 201
103+
104+
def get(self, user_id):
105+
"""
106+
获取用户数据: curl http://127.0.0.1:5000/users/1 -X GET -H "Authorization: token fejiasdfhu"
107+
"""
108+
users = session.query(User).filter(User.id == user_id)
109+
110+
# 用户不存在,返回404
111+
if users.count() == 0:
112+
return None, 404
113+
114+
# 返回用户数据
115+
return get_json(users[0]), 200
116+
117+
def delete(self, user_id):
118+
"""
119+
删除用户数据: curl http://127.0.0.1:5000/users/1 -X DELETE -H "Authorization: token fejiasdfhu"
120+
"""
121+
session.query(User).filter(User.id == user_id).delete()
122+
return None, 204
54123

55124

56125
# 操作(post / get)资源列表
57126
class TodoList(Resource):
127+
# 添加认证
128+
decorators = [auth.login_required]
58129

59130
def get(self):
60-
return ITEMS, 200
131+
"""
132+
获取全部用户数据: curl http://127.0.0.1:5000/users -X GET -d "limit=2&offset=0&sortby=name" -H "Authorization: token fejiasdfhu"
133+
"""
134+
args = parser_get.parse_args()
135+
users = session.query(User)
136+
137+
# 根据条件查询
138+
if "sortby" in args:
139+
users = users.order_by(User.name if args["sortby"] == "name" else User.age)
140+
if "offset" in args:
141+
users = users.offset(args["offset"])
142+
if "limit" in args:
143+
users = users.limit(args["limit"])
144+
145+
# 返回结果
146+
return [get_json(user) for user in users], 200
61147

62148
def post(self):
63-
args = parser.parse_args()
64-
item_id = get_new_item_id()
65-
ITEMS[item_id] = {'name': args['name'], 'age': args['age']}
66-
return ITEMS[item_id], 201
149+
"""
150+
添加一个新用户: curl http://127.0.0.1:5000/users -X POST -d "name=Brown&age=20" -H "Authorization: token fejiasdfhu"
151+
"""
152+
args = parser_put.parse_args()
153+
154+
# 构建新用户
155+
user = User(name=args["name"], age=args["age"])
156+
session.add(user)
157+
session.commit()
158+
159+
# 资源添加成功,返回201
160+
return get_json(user), 201
67161

68162

69163
# 设置路由
70-
api.add_resource(TodoList, '/items')
71-
api.add_resource(Todo, '/items/<item_id>')
164+
api.add_resource(TodoList, "/users")
165+
api.add_resource(Todo, "/users/<int:user_id>")
72166

73167

74-
if __name__ == '__main__':
168+
if __name__ == "__main__":
75169
app.run(debug=True)
170+
171+
172+
""" 常见返回代码
173+
200 OK - [GET]:服务器成功返回用户请求的数据
174+
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功
175+
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
176+
204 NO CONTENT - [DELETE]:用户删除数据成功
177+
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
178+
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)
179+
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的
180+
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作
181+
406 Not Acceptable - [GET]:用户请求的格式不可得
182+
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的
183+
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误
184+
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功
185+
"""

0 commit comments

Comments
 (0)