flask-security ImportError: cannot import name 'db' - flask

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'))

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

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.

Related

Flask Import errors when importing models on pythoneverywhere

Local everything works fine but when I go to pythoneverywhere I get error: Cannot import name "User" from "Data.Domain.User", without Flask-Migrate db works just fine, also, I tried to copy the content of Data.Domain.User module to app.py and it works, the problem appears just when I want to move it on another folder ( location ).
This is what I have in app.py:
from flask import Flask
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager, Server
from flask_migrate import Migrate, MigrateCommand
from flask_login import LoginManager
from flask_mail import Mail, Message
app = Flask(__name__)
app.config['SECRET_KEY'] = 'blablablastrongsecretkey'
app.config['SQLALCHEMY_DATABASE_URI'] = 'connstring'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
login_manager = LoginManager(app)
from Data.Domain.User import User
from Presentation import App
if __name__ == '__main__':
manager.run()
Module Data.Domain.User is this:
from app import db, login_manager
from flask_login import UserMixin
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
uuid = db.Column(db.String(50), unique=True)
username = db.Column(db.String(40), unique=True)
def __repr__(self):
return "User(\n\tID: " + str(self.uuid) + "\n\tUsername: " + self.username + "\n)"
I solved the problem renaming and moving the app.py file in another folder, and then I just imported app from it
from Presentation.Server import app
This is the import from Data.Domain.User file

How do I import a database made with SQLAlchemy to Flask app?

new to web developing. I have three files. database_setup.py with my table classes. restaurant.py used to populate tables and script.py for flask app
database_setup.py
import os
import sys
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class Restaurant(Base):
__tablename__ = 'restaurant'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
engine = create_engine('sqlite:///restaurantmenu.db')
Base.metadata.create_all(engine)
restaurant.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from database_setup import Restaurant, Base, MenuItem
engine = create_engine('sqlite:///restaurantmenu.db')
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
restaurant = Restaurant(name= "Kasra")
session.add(restaurant)
session.commit()
script.py
from flask import Flask
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker,scoped_session
from database_setup import Base, Restaurant
app = Flask(__name__)
engine = create_engine('sqlite:///restaurantmenu.db')
Base.metadata.bind = engine
DBSession = scoped_session(sessionmaker(bind=engine))
session = DBSession()
#app.route('/')
#app.route('/hello')
def HelloWorld():
restaurant = session.query(Restaurant).first()
return restaurant.name
if __name__ == "__main__":
app.run(debug=True)
What I want to do is import the database that was created in restarant.py to script.py. When I run the app it creates a new and empty database because I'm using "engine = "create_engine()" in script.py and I need to use it in order to bind with engine and use session.
The error I get when running app "AttributeError: 'NoneType' object has no attribute 'name'"
Here is what I do to operate with Flask and SQLAlchemy:
In a database.py file:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Base
engine = create_engine(Config.SQLALCHEMY_DATABASE_URI)
session_factory = sessionmaker(bind=engine)
def init_db():
Base.metadata.create_all(bind=engine)
All model classes are located in a models.py file, and are also derived from the declarative base:
Base = declarative_base()
Then the start-up python script contains (key parts only):
app = Flask(__name__)
session = flask_scoped_session(session_factory, app)
if __name__ == "__main__":
database.init_db()
app.run()

Define models in seperate file when using app factory flask

I am creating a flask application where I am using the app factory method. I have a file in the application folder __init__.py which has the create_app function with the following code
def create_app(test_config=None):
app = Flask(__name__,instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:#localhost/database'
db = SQLAlchemy(app)
if test_config == None:
app.config.from_pyfile('config.py',silent=True)
else:
app.config.form_mapping(test_config)
from flaskr import models
try:
os.makedirs(app.instance_path)
except OSError:
pass
class User(db.Model):
id = db.Column(db.Integer,primary_key=True)
uname = db.Column(db.String(50))
#app.route('/hello')
def hello():
return json.dumps({'message':'hello','status':True})
#app.route('/getusers')
def getusers():
u = User.query.get(1)
return json.dumps({'uname':u.uname})
return app
What I want is to define the models in a seperate file. How can I do this?
I have tried defining in a seperate file and importing it. But the problem is the model inherits the db.Model which is then not available in the imported file.
Leave the creation of db object outside create_app without passing any app instance and use the SQLAlchemy.init_app method to configure and init. your db object, this way you can import it from any file:
db = SQLAlchemy()
#...
def create_app(test_config=None):
app = Flask(__name__,instance_relative_config=True)
#...
db.init_app(app)
More on this topic can be found at flask's documentation
I've fought with this problem a few hours too. Couldn't fix it until I came to the realization, that I have to return the app inside the app.app_context()
model.py:
from flask_user import UserMixin
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255, collation='NOCASE'), nullable=False,
unique=True)
# ...
__init__.py:
from flask import Flask
from flask_migrate import Migrate
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_pyfile('config.py', silent=True)
migrate = Migrate()
from appname.model import db
db.init_app(app)
migrate.init_app(app, db)
with app.app_context():
# Create all database tables
db.create_all()
import appname.routes
# Apply the blueprints to the app
from appname import bp1, bp2
appname.register_blueprint(bp1.bp)
appname.register_blueprint(bp2.bp)
return app
Run with:
cd ~/Folder/Webapp
. appname/bin/activate
export FLASK_APP=appname
export FLASK_ENV=development
flask run
(in Linux terminal)
or create this file and run it in python shell:
from appname import create_app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)

Flask Manager Command #manager.command doesn't work

I have a problem here.
I want to populate a table in the database with a record when the migration is executed, but actually it doesn't work. I'm using SQLAlchemy for the DB and the Manager object to create the DB.
In the DB I have a table called 'skills' which contains objects of type 'Skill'.
I just want to fill the table with a record during the initialization phase.
Please help me in finding the solution.
manage.py
#!/usr/bin/env python
import os
from app import create_app, db
from app.models import Skill
from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommand
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
migrate = Migrate(app, db)
def make_shell_context():
return dict(app=app, db=db, Skill=Skill)
manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)
#manager.command
def create_skill():
db.session.add(Skill(name='ciaociao', in_use=False))
db.session.commit()
if __name__ == '__main__':
manager.run()
init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from config import config
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
db.init_app(app)
login_manager.init_app(app)
return app
models.py
class Skill(db.Model):
__tablename__ = 'skills'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), index=True)
in_use = db.Column(db.Boolean, default=False)

How do I add flask-admin to a Blueprint?

for example:
from flask import Flask
from flask.ext.admin import Admin, BaseView, expose
class MyView(BaseView):
#expose('/')
def index(self):
return self.render('index.html')
app = Flask(__name__)
admin = Admin(app)
admin.add_view(MyView(name='Hello'))
app.run()
but, if I need a new file, called 'views.py', how can I add a view into views.py to admin?
Do I need to use a blueprint?
For my project I actually made a child class of Blueprint that supports flask admin:
from flask import Blueprint
from flask_admin.contrib.sqla import ModelView
from flask_admin import Admin
class AdminBlueprint(Blueprint):
views=None
def __init__(self,*args, **kargs):
self.views = []
return super(AdminBlueprint, self).__init__('admin2', __name__,url_prefix='/admin2',static_folder='static', static_url_path='/static/admin')
def add_view(self, view):
self.views.append(view)
def register(self,app, options, first_registration=False):
admin = Admin(app, name='microblog', template_mode='adminlte')
for v in self.views:
admin.add_view(v)
return super(AdminBlueprint, self).register(app, options, first_registration)
For details you may like to read my blog here: http://blog.sadafnoor.me/blog/how-to-add-flask-admin-to-a-blueprint/
I am very late for this question, but anyway... My guess is that you want to use the Application Factory pattern and use the Flask-Admin. There is a nice discussion about the underlying problems. I used a very ugly solution, instantiating the Flask-Admin in the init.py file:
from flask_admin.contrib.sqla import ModelView
class UserModelView(ModelView):
create_modal = True
edit_modal = True
can_export = True
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
# import models here because otherwise it will throw errors
from models import User, Sector, Article
admin.init_app(app)
admin.add_view(UserModelView(User, db.session))
# attach blueprints
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth')
return app
You don't need a blueprint for that. In views.py add an import for the admin object you defined in your main project:
from projectmodule import admin
from flask.ext.admin import BaseView, expose
class MyView(BaseView):
#expose('/')
def index(self):
return self.render('index.html')
admin.add_view(MyView(name='Hello'))
and in your main projectmodule file use:
from flask import Flask
from flask.ext.admin import Admin
app = Flask(__name__)
admin = Admin(app)
# import the views
import views
app.run()
e.g. you add import views after the line that sets admin = Admin(app).
I have an flask app with one blueprint (and login/logout to admin).
This is the best solution I found to implement flask admin with some custom features.
My structure as follows:
my_app
main
__init__.py
routes.py
static
templates
__init__.py
config.py
models.py
run.py
Customized admin index view from models.py
from flask_admin import AdminIndexView
class MyAdminIndexView(AdminIndexView):
def is_accessible(self):
return current_user.is_authenticated
def inaccessible_callback(self, name, **kwargs):
return redirect(url_for('main.home'))
Main init.py as follows:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_admin.menu import MenuLink
from my_app.config import Config
# create extensions
db = SQLAlchemy()
admin = Admin()
def create_app(config_class=Config): # default configutation
app = Flask(__name__)
app.config.from_object(Config)
#initialize extensions
db.init_app(app)
...
# customized admin views
from my_app.models import MyAdminIndexView
admin.init_app(app,index_view=MyAdminIndexView())
admin.add_link(MenuLink(name='Home', url='/'))
admin.add_link(MenuLink(name='Logout', url='/logout'))
#blueprint
from my_app.main.routes import main
app.register_blueprint(main)
return app
I think this is the most elegant solution I came up so far.
In order to keep clean the __init__.py root file:
# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from app.config import Config
from flask_admin import Admin
db = SQLAlchemy()
admin = Admin(template_mode="bootstrap3")
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
admin.init_app(app)
from app.admin import bp
app.register_blueprint(bp, url_prefix='/admin')
return app
Then in the __init__.py of the Blueprint admin app
# app/admin/__init__.py
from flask import Blueprint
from app import admin, db
from app.models import User
from flask_admin.contrib.sqla import ModelView
bp = Blueprint('admin_app', __name__)
admin.name = 'Admin panel'
admin.add_view(ModelView(User, db.session))
# all the rest stuff