Skip to content

一、类视图

使用ViewMethodView这两个类来编写基于类的视图

一、基本使用

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_ruleroute

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)