Flask-RESTful Project Structure

15.7k views Asked by At

Here is what I'm working with:

/myproject
    README.md
    runserver.py
    /myproject
        __init__.py
        api.py
        /resources
            __init__.py
            foo.py
            bar.py
        /common
            __init__.py
            db.py
    /tests
        test_myproject.py

Nothing special here. Most of this can be found on the Intermediate Usage page in the Flask-RESTful User's Guide.

My concern is with circular imports...

api.py

from flask import Flask
from flask_restful import Api

app = Flask(__name__)

from myproject.resources.foo import Foo
from myproject.resources.bar import Bar

api = Api(app)

api.add_resource(Foo, '/Foo', '/Foo/<str:id>')
api.add_resource(Bar, '/Bar', '/Bar/<str:id>')

foo.py

from flask_restful import Resource
from myproject.common.db import query_db


class Foo(Resource):
    def get(self):
        pass
    def post(self):
        pass

db.py

from flask import g
import sqlite3
from myproject.api import app


def get_db():
    db = getattr(g, '_database', None)
    if db is None:
        db = g._database = sqlite3.connect(app.config['DATABASE'])
        db.row_factory = make_dicts
    return db


def query_db(query, args=(), one=False):
    cur = get_db().execute(query, args)
    rv = cur.fetchall()
    cur.close()
    return (rv[0] if rv else None) if one else rv


@app.teardown_appcontext
def close_connection(exception):
    db = getattr(g, '_database', None)
    if db is not None:
        db.commit()
        db.close()

Clearly, I have introduced a circular import into my project:

api.py -> foo.py -> db.py -> api.py

This isn't a big problem as long as I instantiate the Flask application object before I import my resources (which I do). A similar pattern is discussed here (see the Circular Imports section at the bottom of the page).

My question...

Is this a good way to structure a Flask-RESTful project?

This is the closest SO question on this topic that I could find. I was not satisfied with the answer provided because I don't want to keep my database functions in the top level __init__.py file (or in api.py - where the routes belong).

Here are several other similar SO questions, but they are dealing with import errors (which I am not):

Structure Flask-Restful API to use SQLAlchemy

Error importing models when trying to run app

1

There are 1 answers

6
junnytony On

Since your question is largely opinion based, I'm going to suggest what I think would be a better solution :)

Instead of importing myproject.api.app in db.py, I would create my own module level global variable in db.py to store database configuration:

db.py

from flask import g
import sqlite3

_db_config = None # Holds database config

def init(app):
    """ Function must be called to initalize this module """
    global _db_config
    global close_connection
    _db_config = app.config['DATABASE']
    # Manually apply @app.teardown_appcontext decorator
    close_connection = app.teardown_appcontext(close_connection)


def _db_connect():
    if _db_config is None:
        raise Exception('Call init first') # or whatever error you want
    return sqlite3.connect(_db_config)


def get_db():
    db = getattr(g, '_database', None)
    if db is None:
        db = g._database = _db_connect()
        db.row_factory = make_dicts
    return db

....

def close_connection(exception):
    db = getattr(g, '_database', None)
    if db is not None:
        db.commit()
        db.close()

Then in api.py initialize db by calling myproject.common.db.init()

api.py

from flask import Flask
from flask_restful import Api
from myproject.common import db

app = Flask(__name__)
db.init(app)

....

This way db.py no longer has a dependency on api.py