Search code examples
flaskflask-restful

Unable to register a blueprint


CASE UPDATED: 2016-02-10 @ 19:45 UTC

I've been researching for several weeks StackOverflow after reading a couple of books related to Flask, I've been testing and trying different configuration options, playing with several codes and finally I'm forced to write asking for help because I'm totally stuck and I can't go ahead alone. I'm sorry about that.

The problem: I'm not able to register a Blueprint correctly in my app. I'm doing something wrong or something is not being done in the right way. Routes that belong to the blueprint are not working.

The error received: Not Found. The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

I'm trying to build an API with Flask and Flask-Restful. I'm new to Flask and new to Flask-Restful too, so, you can imagine how big is my actual learning curve right now.

At present time, my project has this aspect:

/
├── PROJECT
│   ├── __init__.py
│   └── api_1_0
│       ├── __init__.py
│       ├── resources
│       │   ├── __init__.py
│       │   └── users.py
│       └── routes.py
├── config.py
└── run.py

The Plan The PROJECT (named internally SelfCare) folder will have in the future two or three more packages inside (for web modules belonging to the same project). By now the only one of them is api_1_0.

The idea #1 is to build an API with some basic functionalities. Now, the focus is to present plain messages (return 'hi hi') just to confirm all blocks are working alltogether.

The idea #2 is to build the API as a blueprint inside the SelfCare package. Now i can have only 1 API, but later on i migth have two in parallel running (v1 and v2).

Some days ago i managed to get to work idea 1 but idea 2 is what i cannot get to work. Sorry.

My current files have this contents:

File: run.py

from SelfCare import create_app

app = create_app(config_name='development')

if __name__ == '__main__':
    app.run()

File: config.py

import os


class Config:
    SECRET_KEY = 'whatever'
    APP_DIR = os.path.abspath(os.path.realpath(__file__))
    STATIC_DIR = os.path.join(APP_DIR, 'static')
    IMAGES_DIR = os.path.join(STATIC_DIR, 'images')
    STYLES_DIR = os.path.join(STATIC_DIR, 'styles')
    TEMPLATES_DIR = os.path.join(STATIC_DIR, 'templates')

    # Method with an application instance (for now, just a bypass)
    @staticmethod
    def init_app(app):
        pass

class DevelopmentConfig(Config):
    DEBUG = True

class TestingConfig(Config):
    DEBUG = True

class ProductionConfig(Config):
    DEBUG = False

# Finally, this is a dead simple configuration dictionary
config = {
    # Possible profiles to run under
    'development': DevelopmentConfig,
    'testing': TestingConfig,
    'production': ProductionConfig,
    # Default application profile
    'default': DevelopmentConfig
}

File: PROJECT/init.py

from flask import Flask, render_template
from flask.ext.moment import Moment
from config import config

moment = Moment()

def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    # attach routes and custom error pages here
    @app.route('/')
    def index():
        return 'Here we will start'

    # HERE IT IS SUPPOSED THE BLUEPRINT IS REGISTERED 
    # AND ROUTES FROM BLUEPRINT ARE INCORPORATED TO MAIN PROGRAM
    from .api_1_0 import api as api_blueprint
    app.register_blueprint(api_blueprint)

    return app

File: PROJECT/api_1_0/__init__.py

from flask import Blueprint

api = Blueprint('api', __name__)

from .resources import users

File: PROJECT/api_1_0/routes.py

from .api_1_0 import resources

@resources.route('/user', methods=['GET'])
def users():
    return 'routes.py en blueprint api_1_0'

File: PROJECT/api_1_0/resources/__init__.py

This file is empty (a constructor).

File: PROJECT/api_1_0/resources/user.py

from flask_restful import Resource, reqparse, abort

# With the following dictionrary I do simulate a database connection
USERS = {
    'user1': {'task': 'Build an API'},
    'user2': {'task': 'Coordinate development'},
    'user3': {'task': 'Build core'},
}

def abort_if_user_doesnt_exist(user):
    if user not in USERS:
         abort(404, message="User {} doesn't exist".format(user))

parser = reqparse.RequestParser()
parser.add_argument('task')


class User(Resource):

    def get(self, user):
        abort_if_user_doesnt_exist(user)
        return USERS[user]

    def delete(self, user):
        abort_if_user_doesnt_exist(user)
        del USERS[callsign]
        return '', 204

    def put(self, user):
        args = parser.parse_args()
        task = {'task': args['task']}
        USERS[user] = task
        return task, 201

So, routes.py seems not to work, so I cannot go further in the API. I'm stuck, sorry... Probably the error is just in from of me as a big stone and I cannot see it. Sorry.

Any help will be much more than appreciated. Regards.


Solution

  • You're not registering flask-restful.Api and the REST routes correctly.

    Install Flask-DebugToolbar - it will help you in showing your registered routes.

    Change your code as follows:

    In file config.py add the following two lines to the Config class:

    DEBUG_TOOLBAR_ENABLED = True
    REST_URL_PREFIX = '/api'
    

    In file: PROJECT/init.py change to:

    from flask import Flask, render_template
    from flask.ext.moment import Moment
    from config import config
    
    moment = Moment()
    
    def create_app(config_name):
        app = Flask(__name__)
        app.config.from_object(config[config_name])
        config[config_name].init_app(app)
    
        try:
            from flask_debugtoolbar import DebugToolbarExtension
            DebugToolbarExtension(app)
        except:
            pass
    
        # attach routes and custom error pages here
        @app.route('/')
        def index():
            return render_template('index.html')
    
        # HERE IT IS SUPPOSED THE BLUEPRINT IS REGISTERED
        # AND ROUTES FROM BLUEPRINT ARE INCORPORATED TO MAIN PROGRAM
        from PROJECT.api_1_0 import api_bp, API_VERSION_V1
        app.register_blueprint(
            api_bp,
            url_prefix='{prefix}/v{version}'.format(prefix=app.config['REST_URL_PREFIX'], version=API_VERSION_V1),
        )
        # This registers the actual routes
        import api_1_0.routes
    
        return app
    

    In file: PROJECT/api_1_0/__init__.py change to:

    from flask import Blueprint
    import flask_restful
    
    API_VERSION_V1 = 1
    
    api_bp = Blueprint('api', __name__)
    api_v1 = flask_restful.Api(api_bp)
    

    Map your resources to routes; in file: PROJECT/api_1_0/routes.py change to:

    from PROJECT.api_1_0 import api_v1
    from PROJECT.api_1_0.resources.user import User
    
    api_v1.add_resource(User, '/user/<string:user>')
    

    Your routes will now be something like :

    http://127.0.0.1:5000/api/v1/user/user1 which returns:

    {
        "task": "Build an API"
    }
    

    http://127.0.0.1:5000/api/v1/user/user2 which returns:

    {
        "task": "Coordinate development"
    }
    

    Note how the routes have been built up; the REST_URL_PREFIX from config plus the version number plus the url in the routes.py add_resource.