为什么在Python Flask中使用类vs装饰器?

时间:2012-09-11 13:49:17

标签: python class decorator flask tornado

在Flask中,您可以使用类和装饰器进行路由。 每个用例的优势是什么?

我正在考虑用于静态页面的装饰器和用于更多动态页面的类。 如果我要将龙卷风/ gunicorn与烧瓶结合,这是更好的方法。

我计划使用异步方法,以此为例作为起点: using Flask and Tornado together?

这篇文章指出它可能依赖于框架,但在烧瓶中我们可以同时使用它们 Decorators vs. classes in python web development

2 个答案:

答案 0 :(得分:2)

这些是我个人的经验法则。

  1. 如果我必须从现有应用程序移植,我使用源应用程序中使用的约定。有两种可能的布线方式是一个很大的优势。
  2. 如果应用程序对同一代码使用不同的URL,我会在URL和处理程序类之间创建显式映射。
  3. 如果应用程序中使用的URL和类的数量很少,我将使用装饰器。
  4. 如果应用程序很复杂,使用复杂的URL,我会在URL和处理程序类之间创建一个映射。

答案 1 :(得分:0)

从下面的MVP代码中可以看出,我通常会在实现许多REST API动词时喜欢基于类的路由,这很可能是围绕数据库表之类的资源进行的。使用其他修饰符时,这也有助于提高清晰度,还可以选择哪个动词具有特定修饰符。

或者,我将使用修饰的方法来实现为静态页面返回html的路由,而我实际上只需要GET动词。

from flask import Flask
from flask_restful import Api, Resource, reqparse, abort, fields, marshal_with
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
api = Api(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
db = SQLAlchemy(app)

class VideoModel(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    views = db.Column(db.Integer, nullable=False)
    likes = db.Column(db.Integer, nullable=False)

    def __repr__(self):
        return f"Video(name = {name}, views = {views}, likes = {likes})"

video_put_args = reqparse.RequestParser()
video_put_args.add_argument("name", type=str, help="Name of the video is required", required=True)
video_put_args.add_argument("views", type=int, help="Views of the video", required=True)
video_put_args.add_argument("likes", type=int, help="Likes on the video", required=True)

video_update_args = reqparse.RequestParser()
video_update_args.add_argument("name", type=str, help="Name of the video is required")
video_update_args.add_argument("views", type=int, help="Views of the video")
video_update_args.add_argument("likes", type=int, help="Likes on the video")

resource_fields = {
    'id': fields.Integer,
    'name': fields.String,
    'views': fields.Integer,
    'likes': fields.Integer
}

class Video(Resource):
    @marshal_with(resource_fields)
    def get(self, video_id):
        result = VideoModel.query.filter_by(id=video_id).first()
        if not result:
            abort(404, message="Could not find video with that id")
        return result

    @marshal_with(resource_fields)
    def put(self, video_id):
        args = video_put_args.parse_args()
        result = VideoModel.query.filter_by(id=video_id).first()
        if result:
            abort(409, message="Video id taken...")

        video = VideoModel(id=video_id, name=args['name'], views=args['views'], likes=args['likes'])
        db.session.add(video)
        db.session.commit()
        return video, 201

    @marshal_with(resource_fields)
    def patch(self, video_id):
        args = video_update_args.parse_args()
        result = VideoModel.query.filter_by(id=video_id).first()
        if not result:
            abort(404, message="Video doesn't exist, cannot update")

        if args['name']:
            result.name = args['name']
        if args['views']:
            result.views = args['views']
        if args['likes']:
            result.likes = args['likes']

        db.session.commit()

        return result


    def delete(self, video_id):
        abort_if_video_id_doesnt_exist(video_id)
        del videos[video_id]
        return '', 204


api.add_resource(Video, "/video/<int:video_id>")

if __name__ == "__main__":
    app.run(debug=True)