0

I know this is probably a circular import error but I cannot figure out a solution. The application, so far, is very vanilla. It is the flask-security tutorial with a models.py added. This is in preparation to add blueprints to the project.

from app import db ImportError: cannot import name 'db'

config.py
run.py
---app
------__init__py
------models.py

run.py

from app import app

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

init.py

from config import Config
import app.models

from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_security import Security, SQLAlchemyUserDatastore, \
    login_required

db = SQLAlchemy()

# Create app
def create_app():
    app = Flask(__name__)
    app.config.from_object(Config)
    app.config['DEBUG'] = True
    db.init_app(app)

    user_datastore = SQLAlchemyUserDatastore(db, models.Users,
                                             models.usersRoleNames)
    security = Security(app, user_datastore)

    return app

# Views
@app.route('/')
@login_required
def home():
    return render_template('index.html')

models.py

from app import db
from flask_security import UserMixin, RoleMixin

users_roles = db.Table('users_roles',
        db.Column('user_id', db.Integer(), db.ForeignKey('users.id')),
        db.Column('role_id', db.Integer(), db.ForeignKey('users_role_names.id')))


class usersRoleNames(db.Model, RoleMixin):
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(80), unique=True)
    description = db.Column(db.String(255))


class Users(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255), unique=True)
    username = db.Column(db.String(255))
    password = db.Column(db.String(255))
    last_login_at = db.Column(db.DateTime())
    current_login_at = db.Column(db.DateTime())
    last_login_ip = db.Column(db.String(100))
    current_login_ip = db.Column(db.String(100))
    login_count = db.Column(db.Integer)
    active = db.Column(db.Boolean())
    confirmed_at = db.Column(db.DateTime())
    roles = db.relationship('usersRoleNames', secondary=users_roles,
                            backref=db.backref('users',
                                               lazy='dynamic'))
Jeff Bluemel
  • 476
  • 9
  • 22

2 Answers2

2

Try declaring your db object in models.py and import it in app/__init_.py

models.py

from flask_sqlalchemy import SQLAlchemy                                         

db = SQLAlchemy()
...

app/__init__.py

def create_app():                                                               
    app = Flask(__name__)                                                       
    app.config.from_object(Config)                                              
    app.config['DEBUG'] = True                                                  

    from app.models import db, Users, usersRoleNames                            
    db.init_app(app)                                                                                                             

    user_datastore = SQLAlchemyUserDatastore(db, Users,                         
                                             usersRoleNames)                    
    security = Security(app, user_datastore)                                    

    return app 
Matt Healy
  • 18,033
  • 4
  • 56
  • 56
  • it fixed one problem and created another. thanks for the advice. let me get this part worked out and I will mark it resolved. users_roles = db.Table('users_roles', AttributeError: type object 'SQLAlchemy' has no attribute 'Table'. – Jeff Bluemel Apr 04 '18 at 01:12
1

I have found a simple solution to the circular import problem with flask-security. This solution is particularly useful when the models are declared across different files.


1. Create a folder named models:

All our models will live inside this folder.

Sample folder structure could look like this

models/
├── _db.py
├── __init__.py
├── tickets.py
└── users.py

0 directories, 4 files

2. Create a file named _db.py:

Declare the db object in this file.

The file name could be anything but starting the file name with an underscore ensures that the db object is always imported first when the import statements are sorted.

('VS Code provided an option to automatically sort the imports, so using that doesn't cause any issues.')

_db.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

3. Import db object from _db.py file:

Now we can import the db object from the _db.py file created above:

users.py

from ._db import db
from flask_security import RoleMixin, UserMixin

users_roles = db.Table(
    # Table definition
)

class usersRoleNames(db.Model, RoleMixin):
    # Table definition
    ...

class Users(db.Model, UserMixin):
    # Table definition
    ...

4. Import everything in __init__.py file (optional):

Create a init.py file and import all the models and the db object inside it.

__init__.py

from ._db import db
from .tickets import Tickets
from .users import Users, users_roles, usersRoleNames

'''
Below part is optional 
but I like to do it anyways
'''

__all__ = [
    'db',
    'Tickets',
    'Users',
    'users_roles',
    'usersRoleNames',
]

By importing everything inside the __init__.py file we can import from models folder directly.

So we can do

from .models import db, Tickets, Users

Instead of this

from .models._db import _db
from .models.tickets import Tickets
from .models.users import Users

Just a thought: I am not sure if it's a bad practice to start a python file name with an 'underscore'. If you have an opinion please share your thoughts.

  • 1
    Thank you for responding and posting this. I am no longer using flask-security so I don't have the ability to test this. This project has migrated to Vue.js(Nuxt) as a stand alone client/front end with a flask stand alone/back end. However, the site security has been completely re-written. I would imagine this will be helpful for others though. – Jeff Bluemel Mar 10 '21 at 19:55
  • Glad I could help. – Akshay Shegaonkar Mar 11 '21 at 17:23