切换主题
一、类视图
使用
View
和MethodView
这两个类来编写基于类的视图
一、基本使用
1、基本的视图函数
python
@app.route("/users/")
def user_list():
users = User.query.all()
return render_template("users.html", users=users)
这对用户模型有效,但假设还有更多的模型需要列表页。你需要为每个模型编 写一个视图函数,尽管唯一会改变的是模型和模板名称。 相反,你可以写一个View
子类,它将查询一个模型并渲染一个模板。 作为第一步,我们将把视图转换为一个类,不做任何定制
2、把视图转换为一个类
python
from flask.views import View
class UserList(View):
def dispatch_request(self):
users = User.query.all()
return render_template("users.html", objects=users)
app.add_url_rule("/users/", view_func=UserList.as_view("user_list"))
3、总结
dispatch_request
:等同于定义这是一个类视图,每当类视图接收到请求时都会执行该方法,返回值的设定和视图函数相同app.add_url_rule
:注册url(映射到url)as_view
:创建一个视图函数,传入的字符串就是视图函数的名称
二、url变量传递
python
class DetailView(View):
def __init__(self, model):
self.model = model
self.template = f"{model.__name__.lower()}/detail.html"
def dispatch_request(self, id)
item = self.model.query.get_or_404(id)
return render_template(self.template, item=item)
app.add_url_rule(
"/users/<int:id>",
view_func=DetailView.as_view("user_detail", User)
)
三、视图的生命周期和 self
如果你的视图类需要做很多复杂的初始化,那么为每一个请求做初始化 是没有必要的,而且可能是低效的。
为了避免这种情况,将
view.init.every_reguest
设置为False
,将会只创建一个类的 实例写数据到
self
是不安全的。 如果你需要在请求期间存储数据,那么请使用g
代替
四、视图装饰器
将 View.decorators
设置为一个要应用的装饰 器列表
python
class UserList(View):
decorators = [cache(minutes=2), login_required]
app.add_url_rule('/users/', view_func=UserList.as_view())
如果你没有设置 decorators
,你可以手动应用它们来代替。
这相当于:
python
view = UserList.as_view("users_list")
view = cache(minutes=2)(view)
view = login_required(view)
app.add_url_rule('/users/', view_func=view)
请记住,顺序很重要。如果你习惯于 @decorator
风格,这相当于:
python
@app.route("/users/")
@login_required
@cache(minutes=2)
def user_list():
...
五、基于方法的类视图
1、方法提示
一个常见的模式是用 methods=["GET", "POST"]
注册一个视图, 然后检查 request.method == "POST"
来决定做什么。设置 View.methods
等同于将方法列表传递给 add_url_rule
或 route
。
python
class MyView(View):
methods = ["GET", "POST"]
def dispatch_request(self):
if request.method == "POST":
...
...
app.add_url_rule('/my-view', view_func=MyView.as_view('my-view'))
这等同于下面的内容,只是进一步的子类可以继承或改变这些方法。
python
app.add_url_rule(
"/my-view",
view_func=MyView.as_view("my-view"),
methods=["GET", "POST"],
)
2、方法调度和 API
MethodView
扩展了基本的 View
,以根据请求方法调度类的不同方法。每个 HTTP 方法都会映射到类中具有相同(小写)名称的方法。
MethodView
基于该类定义的方法自动设置 View.methods
。 它甚至知道如何处理子类覆盖或定义其他方法。
我们可以制作一个通用的 ItemAPI
类,提供 get (详细)、 patch (编辑)和删除的方法给一个给定的模型。
一个 GroupAPI
可以 提供 get(列表)和 post(创建)方法。
python
from flask.views import MethodView
class ItemAPI(MethodView):
init_every_request = False
def __init__(self, model):
self.model = model
self.validator = generate_validator(model)
def _get_item(self, id):
return self.model.query.get_or_404(id)
def get(self, id):
item = self._get_item(id)
return jsonify(item.to_json())
def patch(self, id):
item = self._get_item(id)
errors = self.validator.validate(item, request.json)
if errors:
return jsonify(errors), 400
item.update_from_json(request.json)
db.session.commit()
return jsonify(item.to_json())
def delete(self, id):
item = self._get_item(id)
db.session.delete(item)
db.session.commit()
return "", 204
class GroupAPI(MethodView):
init_every_request = False
def __init__(self, model):
self.model = model
self.validator = generate_validator(model, create=True)
def get(self):
items = self.model.query.all()
return jsonify([item.to_json() for item in items])
def post(self):
errors = self.validator.validate(request.json)
if errors:
return jsonify(errors), 400
db.session.add(self.model.from_json(request.json))
db.session.commit()
return jsonify(item.to_json())
def register_api(app, model, name):
item = ItemAPI.as_view(f"{name}-item", model)
group = GroupAPI.as_view(f"{name}-group", model)
app.add_url_rule(f"/{name}/<int:id>", view_func=item)
app.add_url_rule(f"/{name}/", view_func=group)
register_api(app, User, "users")
register_api(app, Story, "stories")
这样产生了以下视图,一个标准的 REST API !
URL | 方法 | 说明 |
---|---|---|
/users/ | GET | 所有用户列表 |
/users/ | POST | 创建一个新用户 |
/users/<id> | GET | 显示一个单独用户 |
/users/<id> | PATCH | 更新一个用户 |
/users/<id> | DELETE | 删除一个用户 |
/stories/ | GET | 显示所有故事 |
/stories/ | POST | 创建一个新故事 |
/stories/<id> | GET | 显示一个单独的故事 |
/stories/<id> | PATCH | 更新一个故事 |
/stories/<id> | DELETE | 删除一个故事 |
六、实操
1、base.py
python
from flask.views import MethodView
from flask import request
from utils.api_mesage import ApiMessage
from exts import db
class ItemAPI(MethodView):
def __init__(self, model, validate_form):
self.model = model
self.validate_form = validate_form
# 根据id查询元素
def get(self, id):
model = self.model.query.get(id)
if model:
return ApiMessage(200, "查询成功", data=model.to_dict()).to_json()
else:
return ApiMessage(404, "未找到资源").to_json()
# 根据id删除元素
def delete(self, id):
model = self.model.query.get(id)
if model:
db.session.delete(model)
db.session.commit()
return ApiMessage(200, "删除成功").to_json()
else:
return ApiMessage(404, "未找到资源").to_json()
# 根据id修改元素
def patch(self, id):
model = self.model.query.get(id)
if model:
form = self.validate_form(request.form)
if form.validate():
for key, value in form.data.items():
setattr(model, key, value)
db.session.commit()
return ApiMessage(200, "修改成功").to_json()
else:
return ApiMessage(400, "修改信息格式有误", data=form.errors).to_json()
else:
return ApiMessage(404, "未找到资源").to_json()
class GroupAPI(MethodView):
def __init__(self, model, validate_form):
self.model = model
self.validate_form = validate_form
# 查询全部元素
def get(self):
models = self.model.query.all()
models_json = [model.to_dict() for model in models]
if models:
return ApiMessage(200, "查询成功", data=models_json).to_json()
else:
return ApiMessage(404, "未找到资源").to_json()
# 新增元素
def post(self):
form = self.validate_form(request.form)
if form.validate():
model = self.model()
for key, value in form.data.items():
setattr(model, key, value)
db.session.add(model)
db.session.commit()
return ApiMessage(200, "新增成功").to_json()
else:
return ApiMessage(400, "新增信息格式有误", data=form.errors).to_json()
def register_api(app, name, item_api, group_api):
app.add_url_rule(f"/{name}/<int:id>", view_func=item_api.as_view(f"{name}-item"))
app.add_url_rule(f"/{name}/", view_func=group_api.as_view(f"{name}-group"))
2、book.py
python
from api.base import ItemAPI,GroupAPI
from model import Book
from utils.form_validate import BookForm
class BookItemAPI(ItemAPI):
def __init__(self):
super().__init__(model=Book,validate_form=BookForm)
class BookGroupAPI(GroupAPI):
def __init__(self):
super().__init__(model=Book,validate_form=BookForm)
3、app.py
python
from api.book import BookItemAPI, BookGroupAPI
from api.base import register_api
register_api(app, "books", item_api=BookItemAPI, group_api=BookGroupAPI)