3

In Flask, you can use classes and decorators for routes. What would be the advantage of each use case?

I was thinking decorators for static pages and classes for more dynamic pages. If I was to combine tornado/gunicorn with flask, which is the better method.

I plan on using async methods, using this as a example as starting point: using Flask and Tornado together?

This post states it may framework dependent, but in flask we can use both.
Decorators vs. classes in python web development

Community
  • 1
  • 1
Merlin
  • 24,552
  • 41
  • 131
  • 206

2 Answers2

2

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.
Hans Then
  • 10,935
  • 3
  • 32
  • 51
  • Is there any technical benefits from mixing them in app. – Merlin Sep 11 '12 at 18:58
  • You mean like in the link explaining why some frameworks use classes and other's decorators? As far as I am aware there are no technical benefits. It is a matter of practicality and taste. In fact, I would not be suprised if the decorator approach, would secretly create a mapping between URLS and handlers. – Hans Then Sep 11 '12 at 23:07
1

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 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)
dr.crusher
  • 31
  • 6
  • Is it possible for one of the functions to have a different signature, for example `post` which doesn't specify a `video_id`? – francistheturd Sep 09 '21 at 15:29