flask 之(六) --- API|RestfulApi
接口概念
IOP:面向接口编程,不再关注具体的实现;只关注输入、输出。
http://www.ruanyifeng.com/blog/2018/10/restful-api-best-practices.html
服务器返回数据类型:
网页数据html,为浏览器使用
Json数据,ajax javascript发请求的一种方式;也可以使用request的python请求方式
为移动端编写接口关注:
接口地址是什么:/xxx/yyy/zzz
接口需要什么参数:参数根据业务场景
返回什么格式的数据:大多数是json数据
RestfulAPI:
一种软件架构风格、设计风格、而不是标准,只是提供了一组设计原则和约束条件。它主要用户客户端和服务器交互类的软件。
基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。REST全程是Representational State Transfer,表征性状态转移。
首次在2000年Roy Thomas Fielding的博士论文中出现,Fielding是一个非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者,
Apache服务器软件的作者之一,Apache基金会的第一任主席。所以,他的这篇论文一经发表,就引起了广泛的关注。
理解RESTful
介绍:https://github.com/RockTeach/PythonCourse/blob/master/web/flask/restful.md
要理解RESTful架构,最好的就是去理解它的单词 Representational State Transfer 到底是什么意思,它的每一个词到底要表达什么。
REST的释义,"(资源的)表现层状态转化",其实这省略了主语。“表现层”其实指的是“资源(Resource)”的“表现层”。
状态码
服务器向用户返回的状态码和提示信息,常见的有以下一些地方
- 200:OK - [GET]:服务器成功返回用户请求的数据
- 201:CREATED -[POST/PUT/PATCH]:用户新建或修改数据成功
- 202:Accepted - [*] :表示一个请求已经进入后台排队(异步任务)
- 204:NO CONTENT - [DELETE]:表示数据删除成功
- 400:INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误
- 401:Unauthorized - [*] :表示用户没有权限(令牌,用户名,密码错误)
- 403:Forbidden - [*]:表示用户得到授权,但是访问是被禁止的
- 404:NOT FOUND - [*]:用户发出的请求针对的是不存在的记录
- 406:Not Acceptable - [*]:用户请求格式不可得
- 410:Gone - [GET] :用户请求的资源被永久移除,且不会再得到的
- 422:Unprocesable entity -[POST/PUT/PATCH]:当创建一个对象时,发生一个验证错误
- 500:INTERNAL SERVER EROR - [*] :服务器内部发生错误
资源(Resource)
所谓“资源”,就是网络上的一个实体,或者说是网络上的一个具体信息。
它可以是一段文本,一张图片,一首歌曲,一种服务,总之就是一个具体的实例。
你可以使用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。
要获取这个资源,访问它的URI就可以了,因此URI就成了每一个资源的地址或独一无二的识别符。所谓“上网”就是与互联网上一系列的“资源”互动,调用它们的URI。
表现层(Representation)
“资源”是一种信息实体,它可以有多种外在表现形式。我们把“资源”具体呈现出来的形式,叫做它的”表现层“(Representation)。
URI只代表资源的实体,不代表它的形式。严格地说,有些网站最后的”.html“后缀名是不必要的,因为这个后缀表示格式,属于”表现层“范畴,而URI应该只代表”资源“的位置。
它的具体表现形式,应该在HTTP请求头的信息中使用Accept和Content-Type字段指定。
状态转换(State Transfer)
访问一个网站,就代表客户端和服务端的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务端。
因此,如果客户端想要操作服务器,就必须通过某种手段,让服务器端发生”状态转换(State Transfer)“。
而这种转换是建立在表现层之上的,所以就是”表现层状态转化“。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议中,四个表示操作方式的动词:GET,POST,PUT,DELETE。
它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可用于更新资源),PUT用来更新资源,DELETE用来删除资源
总结 :
- 每一个URI代表一种资源
- 客户端和服务器之间,传递这种资源的某种表现层
- 客户端通过四个HTTP动词,对服务端资源进行操作,实现”表现层状态转换“
- 同一个url针对用户的不同的请求操作,表现出来的状态是不同的。表现出来的多种形式,就是表现层状态转换。
RESTful是软件架构设计思想。使用在CS,客户端和服务端这种架构模型中。
表现层状态转换
主语 (资源)
URI 每个URI代表一种资源
资源展现给我们的形式就叫做表现层
通过HTTP的请求谓词来实现表现层转换
重要概念
URI、HTTP请求谓词、JSON
注意:
postman/前端 在向服务器提交json数据时,需要声明提交的类型。在postman的请求的headers增加content-type:application/json。
flask 在确认请求数据是通过json提交后,会将json字符产转换成 字典。保存在request.json中
FBV简单体验:
- views.py
from flask import Blueprint, request restful_bp = Blueprint("restful_bp",__name__)
"""
动作 URL 状态码 数据库操作 含义
GET /posts 200 SELECT 从 blog_posts 表中查询一组数据
POST /posts 201 INSERT 向 blog_posts 表中插入一条数据 GET /posts/123 200 SELECT 从 blog_posts 表中查询 id 为123 的记录
PUT /posts/123 200 UPDATE 更新 blog_posts 表中 id 为123 的记录(请求时提供全部字段的更新)
PATCH /posts/123 200 UPDATE 更新 blog_posts 表中 id 为123 的记录(请求时提供部分字段的更新)
DELETE /posts/123 204 DELETE 删除 blog_posts 表中 id 为123 的记录
""" @restful_bp.route("/posts",methods = ["POST","GET"])
def post_list():
if request.method == "POST":
return "向 blog_posts 表中插入一条数据",201 # 201 表示创建成功
elif request.method == "GET":
return "从 blog_posts 表中查询一组数据",200 @restful_bp.route("/posts/<post_id>",methods=["GET","POST","PUT","PATCH","DELETE"])
def post_detail(post_id):
if request.method == "GET":
return "从 blog_posts 表中查询 id 为{} 的记录".format(post_id),200
elif request.method == "PUT":
return "更新 blog_posts 表中 id 为{} 的记录(请求时提供全部字段的更新)".format(post_id),200
elif request.method == "PATCH":
return "更新 blog_posts 表中 id 为{} 的记录(请求时提供部分字段的更新)".format(post_id),200
elif request.method == "DELETE":
return "删除 blog_posts 表中 id 为{} 的记录".format(post_id),204 # 204 表示删除成功
- models.py
from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() class Channel(db.Model):
__tablename__ = "channels" id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(16),unique=True,nullable=False)
sort = db.Column(db.Integer,nullable=False)
# 对象方法。将类的对象形式转化成字典形式
def to_dict(self):
return {
'id': self.id,
'name': self.name,
'sort': self.sort,
}
FBV实现:创建POST | 查询GET
- views.py
- POST:http://127.0.0.1:5000/channel_dict
- GET:http://127.0.0.1:5000/channel_dict
@restful_bp.route('/channel_dict/',methods=["GET","POST"])
def channel_list():
if request.method == 'POST':
# # 结果:b'{\n\t"\xe5\x90\x8d\xe5\xad\x97":"\xe7\xa7\x91\xe6\x8a\x80",\n\t"sort":1\n}'
# # 需在postman中设置一下请求头headers。Content-Type:application/json
# print(request.data)
# # 结果:None
# print(request.json)
if 'name' not in request.json or 'sort' not in request.json:
return abort(400) channel = Channel()
channel.name = request.json['name']
channel.sort = request.json['sort'] db.session.add(channel)
db.session.commit() return jsonify({'channel':channel.to_dict()}),201 elif request.method == "GET":
# 数据库查询的返回的是list类型的数据
_channels = Channel.query.all()
# 将数据库中查询出的对象 转化成 字典形式
channels = [channel.to_dict() for channel in _channels]
# flask视图函数只能返回 str 或 response 对象。可以将数据库查询出来的转化成json,以满足flask视图函数的返回要求
ret = {
'channels':channels
}
# import json
# json_str = json.dumps(ret)
# return json_str
return jsonify(ret)
FBV实现:GET | PUT | PATCH | DELETE
- views.py:http://127.0.0.1:5000/channels/2
@restful_bp.route("/channels/<channel_id>",methods=["GET","POST","PUT","PATCH","DELETE"])
def channel_detail(channel_id):
# 根据条件查询对象
channel = Channel.query.get(channel_id) # 获取信息:http://127.0.0.1:5000/channel
if request.method == "GET":
return jsonify({'channel':channel.to_dict()}),200 # 更新数据:全量更新
elif request.method == "PUT":
if 'name' not in request.json or 'sort' not in request.json:
return abort(400)
channel.name = request.json['name']
channel.sort = request.json['sort']
db.session.add(channel)
db.session.commit()
return jsonify({'channel':channel.to_dict()}),200 # 更新数据:差量更新
elif request.method == 'PATCH':
# name = channel.name
# if 'name' in request.json:
# name = request.json['name']
# channel.name = name
# 等价于 上四行代码。如果有数据更新就更新{先从json中获取数据,如果有就赋值给channel.name},没有数据更新就用原来的当做默认值
channel.name = request.json.get('name',channel.name)
channel.sort = request.json.get('sort',channel.sort)
db.session.commit()
return jsonify({'channel':channel.to_dict()}),200 # 删除数据。删除成功后返回的是空数据。
elif request.method == "DELETE":
db.session.delete(channel)
db.session.commit()
return "",204
Flask-RestfulApi
框架优点:
- 会让代码逻辑更加清晰,不会再有过多的if-else语句
- 框架会提供参数的验证。像form一样提供验证数据的功能
- 框架会提供丰富的输出格式,自定义输出的结构
框架注意:⚠️
- flask-restful 返回字典。框架内部自动转换为json。return {'channels':channels},200
- 如果在扩展文件ext.py实例化和注册路由资源,必须在加载时就注册好,不可以在调用函数懒加载时注册
- 根据请求的同名方法执行同名的视图函数,来完成不同的请求。request.method.lower() 获取请求方式然后将其变成小写
CBV简单体验:pip install Flask-RESTful
# method = request.method
# method = lower(method)
# 获取请求方式后将其转化为小写,然后去类中匹配,如果有同名的函数,就执行相应的请求方法函数 # Resource 是一个资源类,其实就是一个url
# Resource 的父类MethodView是flask自己的views中的一个类,不同Resource我们自己也可以完成各种请求。
# Resource 的父类MethodView也是根据不同的请求方法执行不同的请求函数,来完成各种请求操作
# 源码变小写 meth = getattr(self, request.method.lower(), None)。getattr是从一个实例中将类中的一个方法取出来赋值给一个变量,执行这个变量就是执行此函数方法
CBV类视图
from flask import Flask
from flask_restful import Api, Resource app = Flask(__name__)
api = Api(app)
"""
动作 URL 状态码 数据库操作 含义
GET /posts 200 SELECT 从 blog_posts 表中查询一组数据
POST /posts 201 INSERT 向 blog_posts 表中插入一条数据
"""
class PostList(Resource):
"""
flask-restful 的所有请求是通过 类 来处理的。继承自Resource类。
flask-restful 框架内部会自动根据请求的HTTP METHOD调用同名的实例方法
GET: /posts -> def get(self): pass
POST:/posts -> def post(self): pass
"""
def get(self):
return "从 blog_posts 表中查询一组数据",200 def post(self):
return "向 blog_posts 表中插入一条数据",201 """
动作 URL 状态码 数据库操作 含义
GET /posts/123 200 SELECT 从 blog_posts 表中查询 id 为123 的记录
PUT /posts/123 200 UPDATE 更新 blog_posts 表中 id 为123 的记录(请求时提供全部字段的更新)
PATCH /posts/123 200 UPDATE 更新 blog_posts 表中 id 为123 的记录(请求时提供部分字段的更新)
DELETE /posts/123 204 DELETE 删除 blog_posts 表中 id 为123 的记录
"""
class PostDetail(Resource):
"""
flask-restful 框架内部也支持 URL 上配置的路由参数,路由参数会传递到具体处理请求的实例方法中
"""
def get(self,post_id):
return "从 blog_posts 表中查询 id 为{} 的记录".format(post_id),200 def put(self,post_id):
return "更新 blog_posts 表中 id 为{} 的记录(请求时提供全部字段的更新)".format(post_id),200 def patch(self,post_id):
return "更新 blog_posts 表中 id 为{} 的记录(请求时提供部分字段的更新)".format(post_id),200 def delete(self,post_id):
return "删除 blog_posts 表中 id 为{} 的记录".format(post_id),204 # 路由配置。参数[前两个参数必填]:(resource类(资源类)、访问的url地址、endpoint="相当于一个路由名称 如:blue.login。可以在url_for中使用")
api.add_resource(PostList,'/posts',endpoint='post_list') # 如果不定义endpoint参数,默认endpoint的值是 类名
api.add_resource(PostDetail,'/posts/<int:post_id>',endpoint='post_detail') if __name__ == '__main__':
app.run(debug=True)
# 非restfulAIPI中不用装饰器的写法
# @app.route("/")
def index():
return "Index" # 参数:路由、描述、资源
app.add_url_rule("/index/","index",index) if __name__ == '__main__':
app.run(debug=True)
非API非装饰器写法
简单拆分格式:
- manage.py、ext.py、models.py
from flask_script import Manager
from flask_migrate import MigrateCommand
from app import create_app app = create_app()
manager = Manager(app)
manager.add_command("db",MigrateCommand) if __name__ == '__main__':
manager.run()
manage.py
import os
from flask_migrate import Migrate
from app.models import db migrate = Migrate() def init_db(app):
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(app.root_path, 'sqlite3.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app=app) def init_migrate(app):
migrate.init_app(app=app,db=db)
ext.py
from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() class Channel(db.Model):
__tablename__ = "channels" id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(16),unique=True,nullable=False)
sort = db.Column(db.Integer,nullable=False)
# 对象方法。将类的对象形式转化成字典形式
def to_dict(self):
return {
'id': self.id,
'name': self.name,
'sort': self.sort,
}
models.py
- __innit__.py
from flask import Flask
from flask_restful import Api
from app import ext
from app.apis import ChannelList, ChannelDetail
from app.views import restful_bp def create_app():
app = Flask(__name__) ext.init_db(app)
ext.init_migrate(app) # 注册实例化api扩展
api = Api(app)
# 路由注册
api.add_resource(ChannelList, '/api/ChannelList', endpoint='ChannelList')
api.add_resource(ChannelDetail, '/api/ChannelDetail/<int:id>',endpoint='ChannelDetail')
# 蓝图注册
app.register_blueprint(blueprint=restful_bp)
return app
- api.py 初级[在models.py中定义函数用来字段转字典格式]
from flask_restful import Resource,reqparse
from app.models import Channel, db # 输出格式化。字段自定义验证函数。自定义验证完成后,必须把验证通过后的数值返回出去以方便后续保存操作
def validate_channel_name(value):
if Channel.query.filter_by(name=value).count() > 0:
raise ValueError("频道名重复")
return value # 输入的验证。flask-restful通过reqparse包中的RequestParser类提供客户端请求的数据参数(通常是json)验证校验功能
# 验证需要的参数(客户端提交的需要验证的字段名称,required=True表示必填项,类型,help="错误提示信息")
channel_parser = reqparse.RequestParser()
channel_parser.add_argument('name',required=True,type=validate_channel_name) # type='自定义验证函数名'
channel_parser.add_argument('sort',required=True,type=int,help='sort字段为为必填项,int类型') # http://127.0.0.1:5000/api/ChannelList [get请求、post请求]
class ChannelList(Resource):
"""
GET /channels
POST /channels
"""
def get(self):
# 数据库查询返回的是list类型的数据。需要将从数据库查询到的数据转为字典形式
_channels = Channel.query.all()
channels = [channel.to_dict() for channel in _channels]
# flask中视图函数只能返回str或者response对象。
# flask-restful可以将数据库中查询出来的数据转换成json;以满足flask视图函数的返回要求
# flask-restful返回字典,框架内部自动转换为json。return {'channels':channels},200
res = {
'channels':channels
}
return res,200 def post(self):
# 通过RequestParser的实例对象 验证前端页面传递来的数据。如果验证不通过返回400错误
args = channel_parser.parse_args() channel = Channel()
channel.name = args['name']
channel.sort = args['sort']
db.session.add(channel)
db.session.commit()
# flask-restful 的类视图方法只能返回字典格式
return {'channel':channel.to_dict()},201
- api.py 高级[使用装饰器转字典]
from flask_restful import Resource, reqparse, fields, marshal_with, abort
from app.models import Channel, db def validate_channel_name(value):
if Channel.query.filter_by(name=value).count() > 0:
raise ValueError("频道名重复")
return value
channel_parser = reqparse.RequestParser()
channel_parser.add_argument('name',required=True,type=validate_channel_name)
channel_parser.add_argument('sort',required=True,type=int,help='sort字段为为必填项,int类型') # 通过marshal_with和fields一起完成自定义输出的功能,让api接口可以返回对象,系统内部将对象根据fields的定义格式转换成字段类型
channel_fields = {
"id":fields.Integer,
# url链接。"url": "http://127.0.0.1:5000/api/ChannelDetail/3",
"url":fields.Url(endpoint="ChannelDetail",absolute=True), # absolute=True 表示加上前缀http://127.0.0.1:5000
"name":fields.String,
"sort":fields.Integer,
}
# http://127.0.0.1:5000/api/channels 可以get、post请求
class ChannelList(Resource): @marshal_with(fields=channel_fields)
def get(self):
_channels = Channel.query.all()
return _channels, 200
# 原来写法
# channels = [channel.to_dict() for channel in _channels]
# res = {
# 'channels':channels
# }
# return res,200 # 此装饰器接收一个参数(以什么格式)。有了这个装饰器,在返回的时候对象会按照参数同名所设置的方式变成一个字典返回
@marshal_with(fields=channel_fields)
def post(self):
# 验证前端页面传递来的数据。如果验证不通过返回400错误
args = channel_parser.parse_args() channel = Channel()
channel.name = args['name']
channel.sort = args['sort']
db.session.add(channel)
db.session.commit()
# 原来返回方式
# return {'channel':channel.to_dict()},201
# flask-restful只能返回字典格式类型。添加装饰器marshal_with可以定制输出格式。
return channel,201
# http://127.0.0.1:5000/api/ChannelDetail/3
class ChannelDetail(Resource):
"""
GET /channels/123
PUT /channels/234
PATCH /channels/123
DELETE /channels/123
"""
def get_object(self,id):
channel = Channel.query.get(id)
if channel is None:
return abort(404,message="找不到对象")
return channel @marshal_with(fields=channel_fields)
def get(self,id):
channel = self.get_object(id)
return channel,200 @marshal_with(fields=channel_fields)
def put(self,id):
channel = self.get_object(id)
args = channel_parser.parse_args() # 字段验证 channel.name = args.get("name",channel.name)
channel.sort = args.get("sort",channel.sort)
db.session.commit()
return channel,200 def patch(self,id):
self.put(id) def delete(self,id):
channel = self.get_object(id)
db.session.delete(channel)
db.session.commit()
return "",204
API 1:N 模型
坑:'/api/ArticleDetails/<int:id>' 此id必须和数据库中的相应字段同名,否则匹配不上;.Nested:表示将一个比较复杂的数据对象拆分开,方便转为字典格式
from flask import Flask
from flask_restful import Api
from app import ext
from app.apis import ChannelList, ChannelDetail, ArticleList, ArticleDetail
from app.views import restful_bp def create_app():
app = Flask(__name__) ext.init_db(app)
ext.init_migrate(app) # 注册实例化api扩展
api = Api(app)
# 注册频道路由
api.add_resource(ChannelList, '/api/ChannelLists', endpoint='ChannelLists')
api.add_resource(ChannelDetail, '/api/ChannelDetails/<int:id>',endpoint='ChannelDetails')
# 注册文章路由
api.add_resource(ArticleList, '/api/Articles', endpoint='Articles')
api.add_resource(ArticleDetail, '/api/ArticleDetails/<int:id>',endpoint='ArticleDetails') # 蓝图
app.register_blueprint(blueprint=restful_bp)
return app
__init__.py
import datetime
from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() # 频道 1
class Channel(db.Model):
__tablename__ = "channels" id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(16),unique=True,nullable=False)
sort = db.Column(db.Integer,nullable=False) articles = db.relationship('Article',backref='channel',lazy='dynamic') # 文章 N
class Article(db.Model):
__tablename__ = "articles" id = db.Column(db.Integer,primary_key=True)
created_at = db.Column(db.DateTime,default=datetime.datetime.now())
updated_at = db.Column(db.DateTime,default=datetime.datetime.now(),onupdate=datetime.datetime.now())
title = db.Column(db.String(256),nullable=False)
content = db.Column(db.String(5000),nullable=False) channel_id = db.Column(db.Integer,db.ForeignKey("channels.id"))
models.py
import datetime
from flask_restful import Resource, reqparse, fields, marshal_with, abort
from app.models import Channel, db, Article # ============================ N ===================================
# 自定义一个类,用于时间格式化
class MyDTFmt(fields.Raw):
def format(self, value):
return datetime.datetime.strftime(value,'%Y-%m-%d %H:%M:%S') # 定义参数验证格式
article_parser = reqparse.RequestParser()
article_parser.add_argument('title',required=True,type=str,help="标题必填")
article_parser.add_argument('content',required=True,type=str,help="正文必填")
article_parser.add_argument('channel_id',required=True,type=int,help="频道必填") # 定义返回输出格式
article_fields = {
'id':fields.Integer,
'url':fields.Url(endpoint='ArticleDetails',absolute=True),
'title':fields.String,
'content':fields.String,
# 等同于:'channel':fields.Nested(channel_fields),
'channel':fields.Nested({ # 通过Nested将对象解开
'name':fields.String,
'url':fields.Url(endpoint="ChannelDetails",absolute=True),
"sort": fields.Integer,
}),
'created_at':MyDTFmt, # 进行自定义时间格式化
'updated_at':fields.DateTime(dt_format="iso8601") } # 文章模块。get、post
class ArticleList(Resource): @marshal_with(fields=article_fields)
def get(self):
articles = Article.query.all()
return articles,200 @marshal_with(fields=article_fields)
def post(self):
args = article_parser.parse_args() article = Article()
article.title = args.get('title')
article.content = args.get('content')
article.channel_id = args.get('channel_id') db.session.add(article)
db.session.commit()
return article,201 # 文章模块。get、put、patch、delete
class ArticleDetail(Resource):
def get_object(self,id):
article = Article.query.get(id)
if article is None:
return abort(404,message="找不到对象")
return article @marshal_with(fields=article_fields)
def get(self,id):
article = self.get_object(id)
return article,200 def put(self,id):
pass
def patch(self,id):
pass
def delete(self,id):
pass # ================================== 1 =========================================
# 自定义字段验证函数。 自定义验证后,必须把验证通过后的数值返回出去
def validate_channel_name(value):
if Channel.query.filter_by(name=value).count() > 0:
raise ValueError("频道名重复")
return value channel_parser = reqparse.RequestParser()
channel_parser.add_argument('name',required=True,type=validate_channel_name) # type=自定义验证函数名
channel_parser.add_argument('sort',required=True,type=int,help='sort字段为为必填项,int类型') channel_fields = {
"id":fields.Integer,
"url":fields.Url(endpoint="ChannelDetails",absolute=True),
"name":fields.String,
"sort":fields.Integer,
} channel_article_fields = {
"id": fields.Integer,
"url": fields.Url(endpoint="ChannelDetails",absolute=True),
"name": fields.String,
"articles": fields.List(fields.Nested(article_fields)) # 列表是一个articles对象,通过Nested将其解开,输出出去。
} # 频道模块。get、post
class ChannelList(Resource): @marshal_with(fields=channel_fields)
def get(self):
_channels = Channel.query.all()
return _channels, 200 # 此装饰器接收一个参数(以什么格式)。有了这个装饰器,在返回的时候对象会按照同名参数设置的方式变成一个字典返回
@marshal_with(fields=channel_fields)
def post(self):
# 验证前端页面传递来的数据。如果验证不通过返回400错误
args = channel_parser.parse_args() channel = Channel()
channel.name = args['name']
channel.sort = args['sort']
db.session.add(channel)
db.session.commit()
return channel,201 # 频道模块。get、put、patch、delete
class ChannelDetail(Resource):
"""
GET /channels/123
PUT /channels/234
PATCH /channels/123
DELETE /channels/123
"""
def get_object(self,id):
channel = Channel.query.get(id)
if channel is None:
return abort(404,message="找不到对象")
return channel @marshal_with(fields=channel_article_fields)
def get(self,id):
channel = self.get_object(id) # channel.articles 代表了当前频道下文章列表
return channel,200 @marshal_with(fields=channel_fields)
def put(self,id):
channel = self.get_object(id)
args = channel_parser.parse_args() # 字段验证 channel.name = args.get("name",channel.name)
channel.sort = args.get("sort",channel.sort)
db.session.commit()
return channel,200 def patch(self,id):
self.put(id) def delete(self,id):
channel = self.get_object(id)
db.session.delete(channel)
db.session.commit()
return "",204
高级拆分格式:
- manage.py
""" manage.py """ import os
from flask_migrate import MigrateCommand
from flask_script import Manager
from day05_chaiAdvanced import create_app # 从系统环境中获取参数FLASK_ENV的值给env,判断是什么环境下的服务器
# 避免外人修改代码,方便代码放在什么环境服务器下,就在什么环境下运行
# 在系统终端 vim .bashrc 编写系统环境变量:#FLASK_ENV。export FLASK_ENV = "develop" 保存退出
env = os.environ.get("FLASK_ENV") or 'default' # 首先创建一个flask对象、加载配置、加载扩展库、初始化路由
app = create_app(env) # flask-scripy扩展
manager = Manager(app)
manager.add_command("db",MigrateCommand) if __name__ == '__main__':
manager.run()
- __init__.py
''' 启动项目的__init__.py '''
from flask import Flask from day05_chaiAdvanced.settings import Config, envs
from day05_chaiAdvanced.extension import init_ext
from day05_chaiAdvanced.views import init_api def create_app(env): # 创建Flask对象
app = Flask(__name__,template_folder="../templates") # 加载初始化配置。 从类对象中加载。env参数确定在什么环境下的
app.config.from_object(envs.get(env)) # 加载初始化扩展库。通过懒加载的方式加载(调用函数时参数的传递)
init_ext(app) # 加载初始化api路由器。通过懒加载的方式加载(调用函数时参数的传递)
init_api(app) return app
- views.py
from Book.route import books_api def init_api(app):
books_api.init_app(app)
- books/route.py
from flask_restful import Api from Book.views import BooksResource books_api = Api() books_api.add_resource(BooksResource, "/books/")
- books/views.py
from flask_restful import Resource class BooksResource(Resource): def get(self):
return {"msg": "book ok"}
最新文章
- C# 使用AForge调用笔记本摄像头拍照
- [2014.01.27]wfPng 水印贴图组件 2.1
- 56相册视频(土豆相册视频 激动相册视频 QQ动感影集等)——下载教程
- CentOS 安装
- Make Rules
- nginx的基本配置和虚拟主机的配置
- python--列表生成式--8
- Comparator与Comparable的异同
- 2729:[HNOI2012]排队 - BZOJ
- C# 自定义光标 WaitCursor
- jquery-制作选项卡
- Angular 4 学习笔记 从入门到实战 打造在线竞拍网站 基础知识 快速入门 个人感悟
- Ubantu 16.4 samba安装配置
- 【转载】FPGA算法设计随笔
- 如何使用JavaScript实现纯前端读取和导出excel文件
- springboot 格式化返回日期
- iis7.0 发生未知 FastCGI错误,错误代码 0x8007010b 的解决办法
- 深入浅出 JVM GC(3)
- 在ls命令中使用通配符
- Our Journey of Xian Ends
热门文章
- TXNLP 09-17
- Python之文字转图片
- Practical, Dynamic Visibility for Games(可实现动态显示技术)
- 题解 [SHOI2010]最小生成树
- 解决微信小程序textarea 里输入的文字或者是placeholder里的值,飘到弹出view上
- attr(name|properties|key,value|fn)
- Linux Shell脚本,删除旧文件,保留最新的几个文件
- JavaWeb-SpringBoot(抖音)_二、服务器间通讯
- HDU 5795 A Simple Nim ——(Nim博弈 + 打表)
- [JZOJ5400]:Repulsed(贪心+树形DP)