【问题标题】:Why use classes vs decorators in Python Flask?为什么在 Python Flask 中使用类与装饰器?
【发布时间】:2012-09-04 11:23:33
【问题描述】:

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

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

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

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

【问题讨论】:

  • 有趣的问题,虽然我相信“我在考虑静态页面的装饰器和更多动态页面的类”不是正确的轨道:)
  • 你能不能同时问很多问题?
  • @Jan-PhilipGehrcke,被认为是。
  • 方法不止一种。

标签: python class decorator flask tornado


【解决方案1】:

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

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

【讨论】:

  • 将它们混合在应用程序中是否有任何技术优势。
  • 你的意思是在链接中解释为什么一些框架使用类和其他的装饰器?据我所知,没有技术优势。这是实用性和品味的问题。事实上,如果装饰器方法会秘密创建 URLS 和处理程序之间的映射,我不会感到惊讶。
【解决方案2】:

从下面的 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)

【讨论】:

  • 其中一个函数是否可以具有不同的签名,例如 post 没有指定 video_id
猜你喜欢
  • 2020-07-24
  • 1970-01-01
  • 2021-07-16
  • 2021-05-10
  • 2020-07-15
  • 2020-11-25
  • 2018-02-03
  • 1970-01-01
  • 2010-12-19
相关资源
最近更新 更多