Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to apply decorator to all blueprint urls in flask

I have a blueprint and some url functions,

admin_bp = Blueprint('admin', __name__)

@admin_bp.route('/dashboard', methods=['GET', ])
@flask_login.login_required
def dashboard():

    context = {}

    page = 'admin/dashboard.html'
    return render_template(page, **context)

@admin_bp.route('/deny', methods=['GET', ])
@flask_login.login_required
def deny():
    return 'hey bro you dont belong here'

I don't want to copy paste @flask_login.login_required decorator for all url functions under this blueprint. Is there a better way that I can apply decorator for all blueprint urls?

like image 623
chenxee Avatar asked May 19 '16 12:05

chenxee


2 Answers

You can add before_request() as a function that will run before each request in a view.

You will then want to add decorators to inject additional functionality to the before_request function. You will want to import the login_required decorator to ensure each endpoint requires a logged in user. This decorator is part of the flask_login library.

Since it looks like your views are part of an admin, I'd also recommend adding a custom decorator to your before_request function with something like @role_required('admin'). The functionality for that decorator will live somewhere else and be imported.

@admin_bp.before_request
@login_required
def before_request():
    """ Protect all of the admin endpoints. """
    pass 
like image 92
Robert Avatar answered Sep 20 '22 09:09

Robert


Subclass Blueprint and override the route method.

import flask

class MyBlueprint(flask.Blueprint):
  def route(self, rule, **options):
    def decorator(f):
      # these lines are copied from flask.Blueprint.route
      endpoint = options.pop("endpoint", f.__name__)
      self.add_url_rule(rule, endpoint, f, **options)

      # At this point flask.Blueprint.route simply returns f.
      # But you can nest a decorator.
      def inner(*args, **kwargs):
        # stuff you want to do before each request goes here
        try:
          result = f(*args, **kwargs)
          # stuff you want to do on successful responses (probing status, headers, etc.) goes here
        except Exception as e:
          # stuff you want to do on error responses goes here
          raise
      return inner

Now use the new subclass in your blueprints:

-v1_blueprint = Blueprint('v1', __name__)
+v1_blueprint = MyBlueprint('v1', __name__)

No changes needed to individual routes.

The drawback of this approach is that it copies code from inside Flask. If the implementation of flask.Blueprint.route were to change in a future version, you'd need to sync MyBlueprint with it when you upgrade Flask.

like image 41
Kartik Agaram Avatar answered Sep 17 '22 09:09

Kartik Agaram