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.
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.