Why use classes vs decorators in Python Flask? Why use classes vs decorators in Python Flask? flask flask

Why use classes vs decorators in Python Flask?


These are my personal rules of the thumb.

  1. If I have to port from an existing application, I use the convention that is used in the source application. Having two possible routing styles is a big advantage.
  2. If the application uses different URLs for the same code, I create an explicit mapping between URLs and handler classes.
  3. If the number of URLs and classes used in the application is small, I will use decorators.
  4. If the application is complex, with complex URLs, I create an a mapping between URLs and handler classes.


As seen in the below MVP code, I generally find that I like class based routes when I will implement many of the REST API verbs, most likely around a resource like a database table. This is also beneficial for clarity when using other decorators, and you can also choose which verb may have a certain decorator.

Alternately, I will use a decorated method to implement a route for returning html for static pages, and I only really need the GET verb.

from flask import Flaskfrom flask_restful import Api, Resource, reqparse, abort, fields, marshal_withfrom flask_sqlalchemy import SQLAlchemyapp = 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 '', 204api.add_resource(Video, "/video/<int:video_id>")if __name__ == "__main__":    app.run(debug=True)