Search code examples
pythonflaskurl-routingbackend

Overwrite route in flask blueprint


There is a blueprint with a lot of useful routes defined, but I have no control over it (can not change it's code in any way) Trying to reuse it in a different app but one of the blueprint's endpoints must be overloaded. How can I achieve that?

I tried just adding a new route to blueprint on top of the existing one:

@blueprint.route('/my/route', methods=['PUT', 'POST'])
def my_new_view_func(program, project):
    # some new behavior for the endpoint

As the result there is duplicate url_rule in app.url_map.iter_rules():

<Rule '/my/route' (PUT, POST) -> my_view_func>,
<Rule '/my/route' (PUT, POST) -> my_new_view_func>,

and when requesting /my/route old viewer my_view_func gets executed

Can I somehow get rid of the old url rule? Or maybe there is a better way to overwrite the route?


Solution

  • There are 2 solutions which I found. First:

    from flask import Flask, Blueprint
    
    
    simple_page = Blueprint('simple_page', __name__, )
    
    
    @simple_page.route('/my/route/')
    def my():
        # for example it's a registered route somewhere...
        return 'default'
    
    
    @simple_page.route('/my/route/')
    def new_my():
        # new endpoint / should works instead my()
        return 'new'
    
    # map of views which we won't register in Flask app
    # you can store this somewhere in settings
    SKIP_VIEWS = (
        # route, view function
        ('/my/route/', my, ),
    )
    
    
    class CustomFlask(Flask):
    
        def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
            # Flask registers views when an application starts
            # do not add view from SKIP_VIEWS
            for rule_, view_func_ in SKIP_VIEWS:  # type: str, func
                if rule_ == rule and view_func == view_func_:
                    return
            return super(CustomFlask, self).add_url_rule(rule, endpoint, view_func, **options)
    
    
    app = CustomFlask(__name__)
    app.register_blueprint(simple_page)
    app.run(debug=True)
    

    Second way:

    two.py - default blueprint with endpoint

    from flask import Blueprint
    
    bp_two = Blueprint('simple_page2', __name__, )
    
    
    @bp_two.route('/my/route/')
    def default():
        return 'default'
    

    test.py - your blueprint + app

    from flask import Flask, Blueprint
    
    from two import bp_two
    
    your_bp = Blueprint('simple_page', __name__, )
    
    
    @your_bp.route('/my/route/')
    def new_route():
        return 'new'
    
    
    app = Flask(__name__)
    # register blueprint and turn off '/my/route/' endpoint
    app.register_blueprint(bp_two, **{'url_defaults': {'/my/route/': None}})
    app.register_blueprint(your_bp)
    
    app.run(debug=True)
    

    Run app. Open /my/route/. You will see that default endpoint wasn't add/works.

    Hope this helps.