flask-rest-jsonapi: Unable to add blueprints without errors

I don’t know if this is an issue or just my ignorance but I am having difficulty adding my api routes with a blue print prefix:

app/api/views:

from app.api.resources import UserList, UserDetail, MaterialList, MaterialDetail
from flask import Blueprint
from flask_rest_jsonapi import Api

json_api = Blueprint('api', __name__, url_prefix='/api')  # @todo figure out how to add blueprints
api = Api(blueprint=json_api)

# User routes
api.route(UserList, 'user_list', '/users')
api.route(UserDetail, 'user_detail', '/users/<string:id>')

# Material routes
api.route(MaterialList, 'material_list', '/materials')
api.route(MaterialDetail, 'material_detail', '/materials/<string:id>')

app/init.py

# -*- coding: utf-8 -*-
from flask import Flask
from app.api.views import api
from app.database import db
from app.cors import cors
from app.auth.views import auth


def create_app():
    app = Flask(__name__)
    app.config.from_object('config')
    db.init_app(app)
    cors.init_app(app)
    api.init_app(app)
    app.register_blueprint(auth)
    return app


def setup_database(app):
    with app.app_context():
        db.create_all()


app = create_app()
setup_database(app)

Now if I visit http://localhost:5000/api/materials I receive the following error:

werkzeug.routing.BuildError: Could not build url for endpoint ‘material_list’. Did you mean ‘api.material_list’ instead?

The obvious solution that springs to mind would be to change the endpoint name in routes and schemas to: api.material_list

however the error is now:

werkzeug.routing.BuildError: Could not build url for endpoint ‘api.material_list’. Did you mean ‘api.api.material_list’ instead?

If I remove the blueprint:

api = Api()

and visit: http://localhost:5000/materials everything works perfectly!

I’ve tried defining the blueprint in every way conceivable but I’m still getting the same error. If this is not an issue and I’m just being a noob please forgive me.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 1
  • Comments: 18

Most upvoted comments

I have seen in your skeleton app that your schemas are not setup correctly. If you want to use a blueprint like that Blueprint('api', __name__, url_prefix='/api') you must add the api prefix to all your endpoints in your schemas. So self_view = 'person_detail' becomes self_view = 'api.person_detail'. First update your schemas and tell me if it fix the issue. Note that I’m talking about about the api prefix, the first parameter of your blueprint not the url prefix. For example: Blueprint('api_prefix', __name__, url_prefix='/api') your schema must looks like this self_view = 'api_prefix.person_detail'.

Just share the exception that is being thrown, may be I will be able you help you better.

from flask import Blueprint
from flask_json_api import Api

api = Api(blueprint=Blueprint('api', __name__, url_prefix='/api'))

Now when you are declaring schemas everywhere for view_name, prefix with api..

class ExampleSchema(Schema):
    class Meta:
         type_ = 'example'
         self_view = 'api.example_detail'

Similarly in relationships too, but not while declaring routes.

api.route(ExampleDetail, 'example_detail', '/example/<int:id>')

Then I guess there would be no problem. If you are still seeing 404s then try by not passing the app while declaring the api extension and instead after declaring all the routes, do init_app.

# Instead of
api = Api(app, blueprint=Blueprint('api', __name__, url_prefix='/api'))
api.route(ExampleDetail, 'example_detail', '/example/<int:id>')
# Use this
api = Api(blueprint=Blueprint('api', __name__, url_prefix='/api'))
api.route(ExampleDetail, 'example_detail', '/example/<int:id>')
api.init_app(app)

I don’t know why this happens, but after doing some hit and trials after a full revamp of my code, I discovered this was the problem for which I spent quite some time before I figured it out by myself.

If you need any guidance for project structure, this is what I am following at present after several rounds of rework:

project/
    __init__.py
    models
        __init__.py   -> import all the models under this package and make them available on top
        group1.py   -> group of models, which might have same base model will help in code reduction
        group2.py
        group3.py
    schemas
        __init__.py   -> import all the schemas under this package and make them available on top
        group1.py   -> group of schemas, which might have same base schema
        group2.py
        group3.py
    resources
        __init__.py   -> import all the resources
        group1.py   -> use the models, schemas to declare resources and routes
        group2.py
        group3.py

If you don’t have groups you can also declare models and schemas as modules instead of packages. This structure reduces cyclic dependencies all together (the thing that bugged me a lot), since model declarations doesn’t require other models or schemas or resources. Same goes with schema declaration. But resources that need both, can use them independently and no one resource depends on other resource, no issues of cyclic dependency at all.

What I further suggest is to make a helper class Endpoint as utils. Declaring routes like api.route will become a lot tiring process and it will anyways look ugly at end. Try to make your routes look like a dictionary or mapping of views to resource and routes (better readability). Then finally import all such endpoint mappings and declare your routes together under a loop.

This is a very common use case, I figured it out from the issue above. An example would be good in the docs.