2

I am trying to design a simple api using flask, flask-restplus, and flask-pymongo but I'm facing one structural design, based on import and variables sharing, I cannot get access to the db in any way.

Here is my code in my main engine file:

app = Flask(__name__)
db = PyMongo(app)

api = Api(app)

from auth import namespace as ns1
api.add_namespace(registerNamespace.api)


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

But at the same time, I would like to get access to the db instance in actual api implementation:

from engine import engine

api = Namespace('register', description="Registration")

db = engine.db

@api.route('/whatever')
Class Whatever():
 def get(self):
    db.doSomething();
    return "Simple getter"

I get the following error.

ImportError: cannot import name engine

I've been trying to fix this in quite a long time, because I don't think it is that stupid of a structural decision but I probably am wrong. Which ways, either structural or import wise could help fixing this?

Thank you in advance!

Oluwafemi Sule
  • 36,144
  • 1
  • 56
  • 81
Mumrau
  • 62
  • 7
  • It appears that I've found a way to make it "work", which is to put the imports inside the get(self): function, but to be quite honest, this looks absolutely terrible... – Mumrau Jan 18 '18 at 15:27

1 Answers1

3

There is myriad of approaches to architect your flask application for easy resource sharing. I however favor binding resources to a common package usually the application package such that other modules can import resources from that package.

Say we have a fictitious project named demo with the following structure:

.
├── api
│   ├── __init__.py
│   └── namespace1.py
└── demo
    ├── __init__.py
    ├── main.py
    └── setup.py

Notice that we have api separate as its own package.

Here is a brief description of the contents of individual module.

demo/__init__.py

db = None # initials package level name to None. 

demo/setup.py

from flask import Flask
from flask_pymongo import PyMongo

import demo

app = Flask('demo')
demo.db = PyMongo(app)  # Here is where we bind the shared resource to the package level name.

demo/main.py

from demo.setup import app
from api import register_api

register_api(app)

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

Our API can import from demo easily:

api/namespace1.py

from flask_restplus import Namespace, Resource, fields

from demo import db  # Look ma!, it's easy to share resources.
...

@ns.route('/')
class CatList(Resource):
    @ns.doc('list_cats')
    @ns.marshal_list_with(cat)
    def get(self):
        '''List all cats'''
        print(db)
        return CATS

You can view the complete sample here.

Mumrau
  • 62
  • 7
Oluwafemi Sule
  • 36,144
  • 1
  • 56
  • 81
  • 1
    Awesome that's precisely what I was looking for. I had no idea about putting stuff in my __init__.py, I was thinking it was a pretty bad practice. Thank you so much. – Mumrau Jan 24 '18 at 18:20
  • Some errors in the architecture make this not functionnal. In demo/setup.py you import the demo package it belongs to. – c24b Dec 04 '19 at 09:54
  • What errors weret they? Did you checkout the accompanying repository I linked. When running the app, you would have to remember specify `PYTHONPATH` to be path containing both packages (`api` and `demo`). Python loads `__init__.py` before running `demo/main.py`. Fwiw, you could also extract shared resources in their own separate module if you find that to be better. – Oluwafemi Sule Dec 04 '19 at 16:28