CraigH
CraigH

Reputation: 2061

Using Flask Restful as a Blueprint in Large Application

I am trying to use Flask restful as a Blueprint in a pattern that works for other blueprints. I keep getting the following error message

I get the following error message

AttributeError: 'Blueprint' object has no attribute 'add_resource'

My project setup is as follows:

Folder structure

├── app
│   ├── __init__.py
│   ├── api
│   │   ├── __init__.py
│   │   └── routes.py
│   ├── main
│   │   ├── __init__.py
│   │   ├── forms.py
│   │   └── views.py
│   └── templates
│       ├── base.html
│       └── home.html
├── config.py
├── manage.py
└── requirements.txt

__init__.py

from flask import Flask
from flask_restful import Api
from flask_bootstrap import Bootstrap
from config import config
bootstrap = Bootstrap()
api = Api()

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

    bootstrap.init_app(app)
    api.init_app(app)

    from .main import main as main_blueprint
    from .api import api as api_blueprint
    app.register_blueprint(main_blueprint)
    app.register_blueprint(api_blueprint)
    return app

api/__init__.py

from flask import Blueprint

api = Blueprint('api', __name__)

from . import routes

api/routes.py

from flask_restful import Resource
from . import api

class TodoItem(Resource):
    def get(self, id):
        return {'task': 'Say "Hello, World!"'}

api.add_resource(TodoItem, '/todos/<int:id>')

What am I doing wrong??

Upvotes: 5

Views: 17858

Answers (3)

Joe Gasewicz
Joe Gasewicz

Reputation: 1485

If you follow the instructions from https://flask-restful.readthedocs.io/en/0.3.5/intermediate-usage.html

The key points here are to create a Flask Blueprint instance & pass it to a new instance of flask-restfuls's Api class.

Lastly, make sure to register the flask-restful api blueprint within your create_app function: app.register_blueprint(api_bp)

from flask import Flask, Blueprint
from flask_restful import Api
from flask_bootstrap import Bootstrap
from config import config

bootstrap = Bootstrap()
api_bp = Blueprint('api', __name__)
api = Api(api_bp)

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

   bootstrap.init_app(app)

   from .users import main as users_blueprint
   from .blogs import main as blogs_blueprint

   # blueprints for blogs & users
   app.register_blueprint(users_blueprint)
   app.register_blueprint(blogs_blueprint)
   app.register_blueprint(api_bp)
   
   return app

Also note, you don't need to register api.init_app(app) anymore.

Upvotes: 7

Karolius
Karolius

Reputation: 581

If you want to have submodules (like your /api) based on resources...

Eg: folder structure

├── app
│   ├── __init__.py
│   ├── foo
│   │   ├── __init__.py
│   │   └── routes.py
│   ├── boo
│   │   ├── __init__.py
│   │   └── routes.py
├── config.py
├── manage.py

... and register their blueprints with url_prefix to not repeat the common part in each added resource. Create new Api instance in each module and pass to it a blueprint.

foo/__init__.py

from flask import Blueprint                            
from flask_restful import Api
                                                       
foo_bp = Blueprint('foo', __name__, url_prefix='/foo') 
foo_api = Api(foo_bp)  
                                                                                     
from . import routes 

in routes import foo_api and add resources to it

foo/routes.py

from flask_restful import Resource
from . import foo_api

class TodoItem(Resource):
    def get(self, id):
        return {'task': 'Say "Hello, World!"'}

foo_api.add_resource(TodoItem, '/todos/<int:id>')
                          

Then in main application __init__.py just import modules blueprints and register it. You dont even need to add a "main" application blueprint. If you would import the api from main application __init__ then you can't register each blueprint with it's own params like url_prefix.

__init__.py

from flask import Flask
from config import config

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

   from .foo import foo_bp
   from .boo import boo_bp

   app.register_blueprint(foo_bp)
   app.register_blueprint(boo_bp)

   return app

You can set the url_prefix on register blueprint (it has priority) or when you create it. To check routes you can print the app.url_map

Upvotes: 4

Matt Healy
Matt Healy

Reputation: 18531

You're running in to trouble because of how you've named your blueprint api, whilst also using the api object from flask_restful. In your routes.py you're explicitly importing api from api/__init__.py, and this is a Blueprint object. You can't call add_resource to a Blueprint object, only to an Api object from flask_restful.

If you change your import to:

from .. import api

you'll be importing the correct object. I'd still recommend changing your blueprint name anyway to avoid this sort of confusion.

Upvotes: 2

Related Questions