I am working on adding flask admin to a preexisting flask boiler plate project. I've been able to get the basic project working at https://github.com/kc1/flask-base (SCREENSHOT). I now need to add modelviews to add basic CRUD functionality. To do this I changed the code to :
adm = Admin(app,name='flaskadmin')
from app.models import User
adm.add_view(ModelView(User, db.session))
You can see that it works. But if I import the User model with the rest of the imports at the top of app/init I get:
Traceback (most recent call last):
File "...flask-base/manage.py", line 10, in <module>
from app import create_app, db
File "E:\ENVS\r3\flask-base\app\__init__.py", line 17, in <module>
from app.models import User
File "E:\ENVS\r3\flask-base\app\models\__init__.py", line 6, in <module>
from .user import * # noqa
File "E:\ENVS\r3\flask-base\app\models\user.py", line 7, in <module>
from .. import db, login_manager
ImportError: cannot import name 'db'
Why?
User is a Flask-SQLAlchemy model that wraps models using SQLalchemy's API. It inherits all of its models from a db object, which I assume you are instantiating or registering inside a create_app method.
So, you should have something like this
db = SQLAlchemy()
def create_app(config):
app = Flask(__name__)
db.init_app(app)
adm = Admin(app,name='flaskadmin')
from app.models import User
adm.add_view(ModelView(User, db.session))
return app
Whenever you import User from user.py you are basically importing db.Model, which requires for db to exists and actually contain data. Be wary of circular imports in Flask and in Python in general.
The error you are receiving is clearly stated in the error traceback
File "E:\ENVS\r3\flask-base\app\models\user.py", line 7, in <module>
from .. import db, login_manager
ImportError: cannot import name 'db'
This is, in user.py there is an import of db from ../__init__.py but in that file there is an import of User happening before the definition of db.
A db object emulates the declarative approach from SQLAlchemy, where the object holds data about every other Class that inherited from it.
Related
I am building a small questionnaire with Flask and SQLAlchemy, and for development testing I try to recall a random form by it's id. The terminal gives an ImportError however, but I do not have any files named after modules (like other stackoverflow problems) or circular import as the error suggests. How can I query the AddEnqueteForm in this separate file?
enquete_algorithm.py:
from models import AddEnqueteForm
import random
from settings import *
from flask import request
def enq_results(form_id):
form = AddEnqueteForm.query.all(filter=form_id)
reliability_dict = {}
reliability_dict['stelling_set_1'] = algorithm(form.stelling1, form.stelling2)
And of course models.py:
from main import db
class AddEnqueteForm(db.Model):
id = db.Column(db.Integer, primary_key=True)
stelling1 = db.Column(db.String(4), nullable=False)
stelling2 = db.Column(db.String(4), nullable=False)
Error:
Traceback (most recent call last):
File "c:\Users\Enquete\enquete_algorithm.py", line 1, in <module>
from mods import AddEnqueteForm
File "c:\Users\Enquete\models.py", line 1, in <module>
from main import db
File "c:\Users\Enquete\main.py", line 9, in <module>
from routes import *
File "c:\Users\Enquete\routes.py", line 4, in <module>
from models import AddEnqueteForm
ImportError: cannot import name 'AddEnqueteForm' from partially initialized module 'models' (most likely due to a circular import) (c:\Users\Enquete\models.py)
I realized some variable names are a bit confusing: the AddEnqueteForm is a model, not a form. I built the forms entirely in HTML.
The problem is not with enquete_algorithm.py .The circular import is occurs between routes.py and models.py. You are importing main.py in models.py and main.py in place imports routes.py and routes.py is importing models.py.
For solving this I made a separate file database.py where I define the db and I import db from that file in both main.py and models.py. This prevents the circular import.
database.py:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def init_app(app):
db.init_app(app)
models.py :
from database import db
class AddEnqueteForm(db.Model):
id = db.Column(db.Integer, primary_key=True)
stelling1 = db.Column(db.String(4), nullable=False)
stelling2 = db.Column(db.String(4), nullable=False)
In main.py you need to import both models and database and use init_app() to initialize the database.
[...]
from database import db
from models import *
[...]
app = Flask(__name__)
database.init_app(app)
Now if you ever want to run command db.create_all() through terminal to create database. you need to use application context.
running the following commands will create the database.
from main import *
with app.app_context():
db.create_all()
I'm trying to create a Blueprint and ran into this problem:
Traceback (most recent call last):
File "C:\Users\Max\PycharmProjects\python1\flask_first\__init__.py", line 3, in <module>
from models import db
File "C:\Users\Max\PycharmProjects\python1\flask_first\models.py", line 5, in <module>
current_app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.sqlite3.html' # access to the SQL
File "C:\python3.9\lib\site-packages\werkzeug\local.py", line 347, in __getattr__
return getattr(self._get_current_object(), name)
File "C:\python3.9\lib\site-packages\werkzeug\local.py", line 306, in _get_current_object
return self.__local()
File "C:\python3.9\lib\site-packages\flask\globals.py", line 52, in _find_app
raise RuntimeError(_app_ctx_err_msg)
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
I've already done a lot of research and nothing works for me (or I'm just not looking properly enough).
This is the models.py code:
from flask_sqlalchemy import SQLAlchemy
from flask import current_app
current_app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.sqlite3.html' # access to the SQL
current_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(current_app)
class users(db.Model):
__tablename__ = 'users'
_id = db.Column('id', db.Integer, primary_key=True)
name = db.Column(db.String(80))
email = db.Column(db.String(120))
password = db.Column(db.Integer)
def __init__(self, name, email, password):
self.name = name
self.email = email
self.password = password
And this is the __init__.py:
from datetime import timedelta
from flask import Flask
from models import db
from flask_first.admin.second import second
def create_app():
app = Flask(__name__)
with app.app_context():
db.init_app(app)
return app
create_app.secret_key = 'hello world'
create_app.permanent_session_lifetime = timedelta(minutes=5) # setting the time for long-lasting session
if __name__ == '__main__':
db.create_all()
create_app.run(debug=True)
Here is a screenshot of my structure:
Here I'll expand on my comment into an answer.
Python executes your code line-by-line, and that includes import statements. As the error indicates, when it entered __init__.py and got to the from models import db line, it immediately jumped to models.py, and started executing your lines there.
Traceback (most recent call last):
File "...\__init__.py", line 3, in <module>
from models import db
File "...\models.py", line 5, in <module>
current_app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.sqlite3.html'
At this point, the imported current_app does not exist yet, because the create_app from __init__.py seems to have not been called yet. This is where you'll get the common Flask error of "working outside of application context:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
Most of the time you get this type of error, the solution is to reorder your initialization codes. Make sure that the Flask app instance is already created before you do anything else. It should usually be the first thing your code does.
Following the Quickstart tutorial from flask-sqlalchemy, you can put the db object initialization near the app initialization.
# Create app object
app = Flask(__name__)
# Set configs
app.config['...'] = ...
# Create db object
db = SQLAlchemy(app)
The app and db objects typically reside together in some top-level main module. Then, in your other sub-modules (controllers, models, etc.) where you need to setup each component separately, import the app and/or db from the main module:
from some_main_module import app, db
# Do stuff with app and db
I am trying to connect to an existing database and I am unsure where to put db.Model.metadata.reflect(db.engine) as shown in the following question.
My flask app that is structured like:
I am not sure where to add db.Model.metadata.reflect(db.engine).
In intel\__init__.py I am using a function to create app:
def create_app():
app = Flask(__name__)
app.config.from_object('config')
register_extensions(app)
register_blueprints(app)
return app
And in my register_extensions function:
def register_extensions(app):
db.init_app(app)
db.Model.metadata.reflect(db.engine)
return None
in my model\sam.py
from intel.database import db
class Sam(db.Model):
__table__ = db.Model.metadata.tables['sam_monthly_extract']
Now, when I do that I get an error:
Traceback (most recent call last):
File "run.py", line 1, in <module>
from intel import create_app
File "C:\Users\spitf\G2XChange\intel_app\intel\__init__.py", line 6, in <module>
from intel.views.sam_view import sam_blueprint
File "C:\Users\spitf\G2XChange\intel_app\intel\views\sam_view.py", line 3, in <module>
from intel.models.sam import Sam
File "C:\Users\spitf\G2XChange\intel_app\intel\models\sam.py", line 4, in <module>
class Sam(db.Model):
File "C:\Users\spitf\G2XChange\intel_app\intel\models\sam.py", line 5, in Sam
__table__ = db.Model.metadata.tables['sam_monthly_extract']
KeyError: 'sam_monthly_extract'
What am I doing wrong?
Edit:
run.py
from intel import create_app
app = create_app()
app.run(debug=True)
I came across your question in my search for an answer to the same exact problem and since it helped me actually get there, I had to make sure to come back and provide you with what helped solve my issue.
What I think was happening was that the __init__.py that contained the application factory was imported from my run.py and so was the import of the models.py which was at the bottom of the __init__ file, which imported before I could run the function to create the app and not having any context from which to pull that table from. That's at least the best I can explain it currently.
What solved it for me was to move the models import into the application factory, explicitly under the app context, right after db.init_app() like so:
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
with app.app_context():
db.Model.metadata.reflect(db.engine)
from app import models
Not sure if that is the best way to do it, but it's been working for me ever since.
Whenever I try to import init db in the python console:
>>from app.database import init_db
>>init_db()
init db
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "app\database.py", line 18, in init_db
import models
ImportError: No module named models
This is the content of my app.database function:
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from config import SQLALCHEMY_DATABASE_URI
engine = create_engine(SQLALCHEMY_DATABASE_URI, convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
def init_db():
# import all modules here that might define models so that
# they will be registered properly on the metadata. Otherwise
# you will have to import them first before calling init_db()
print "init db"
import models
Base.metadata.create_all(bind=engine)
Why is it like this?
UPDATE: This is the structure of my project.
app/
filebase/
init.py
models.py
users/
init.py
models.py
projects/
init.py
models.py
init.py
database.py (this is where the init_db was defined
As for the structure on why it looked like that, the reason is to make the project modular in its form.
You need the parent directory of models.py (or, of the models package) to be in your sys.path. You can put it there in your console session, or, in the shell, add it to environment variable PYTHON_PATH so it will be set at entry to the console session.
I'm having trouble with the creation of blueprints by Flask-Admin when I'm testing my app.
This is my View class (using SQLAlchemy)
##
# All views that only admins are allowed to see should inherit from this class.
#
class AuthView(ModelView):
def is_accessible(self):
return current_user.is_admin()
class UserView(AuthView):
column_list = ('name', 'email', 'role_code')
This is how I initialize the views:
# flask-admin
admin.add_view(UserView(User, db.session))
admin.init_app(app)
However, when I try to run more then one test (the fault always occurs on the second test and all the other tests that follow), I always get following error message:
======================================================================
ERROR: test_send_email (tests.test_views.TestUser)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/lib/python2.7/site-packages/nose/case.py", line 133, in run
self.runTest(result)
File "/lib/python2.7/site-packages/nose/case.py", line 151, in runTest
test(result)
File "/lib/python2.7/site-packages/flask_testing.py", line 72, in __call__
self._pre_setup()
File "/lib/python2.7/site-packages/flask_testing.py", line 80, in _pre_setup
self.app = self.create_app()
File "/tests/test_init.py", line 27, in create_app
app = create_app(TestConfig)
File "/fbone/app.py", line 41, in create_app
configure_extensions(app)
File "/fbone/app.py", line 98, in configure_extensions
admin.add_view(UserView(User, db.session))
File "/lib/python2.7/site-packages/flask_admin/base.py", line 484, in add_view
self.app.register_blueprint(view.create_blueprint(self))
File "/lib/python2.7/site-packages/flask/app.py", line 62, in wrapper_func
return f(self, *args, **kwargs)
File "/lib/python2.7/site-packages/flask/app.py", line 885, in register_blueprint
(blueprint, self.blueprints[blueprint.name], blueprint.name)
AssertionError: A blueprint's name collision occurred between <flask.blueprints.Blueprint object at 0x110576910> and <flask.blueprints.Blueprint object at 0x1103bd3d0>. Both share the same name "userview". Blueprints that are created on the fly need unique names.
The strange thing is that this only happens on the second test and never when I just run the app.
When I debugged the tests, the first time it did exactly what I expected and added the blueprint to the app after the init_app(app). The second time however the process immediately stopped when reaching the add_view step (which I think is strange because the blueprints get registered in the init_app(app) call?)
The same thing happened to me while using Flask-Admin and testing with pytest. I was able to fix it without creating teardown functions for my tests by moving the creation of the admin instance into the app factory.
Before:
# extensions.py
from flask.ext.admin import Admin
admin = Admin()
# __init__.py
from .extensions import admin
def create_app():
app = Flask('flask_app')
admin.add_view(sqla.ModelView(models.User, db.session))
admin.init_app(app)
return app
After:
# __init__.py
from flask.ext.admin import Admin
def create_app():
app = Flask('flask_app')
admin = Admin()
admin.add_view(sqla.ModelView(models.User, db.session))
admin.init_app(app)
return app
Because pytest runs the app factory each time it no longer tries to register multiple views on a global admin instance. This isn't consistent with typical Flask extension usage, but it works and it'll keep your app factory from stumbling over Flask-Admin views.
I had to add the following to my test case tearDown. It cleans up the views that were added to the admin extension in the test setup
from flask.ext.testing import TestCase
from flask.ext.admin import BaseView
# My application wide instance of the Admin manager
from myapp.extensions import admin
class TestView(BaseView):
...
class MyTestCase(TestCase):
def setUp(self):
admin.add_view(TestView())
def tearDown(self):
admin._views.pop(-1)
admin._menu.pop(-1)
This is certainly a bit of a hack, but it got the job done while I had this problem.
Just in case this helps anyone,
another way to handle this is to do:
class MyTestCase(TestCase):
def setUp(self):
admin._views = []
this way you don't have to set the Admin() initialization inside the factory. It seems more appropiate to me.
It work out in this way. just for your reference.
#YourApp/init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
db = SQLAlchemy()
admin = Admin(name='TuozhanOA', template_mode='bootstrap3')
def create_app(config_class=Config):
app = Flask(name)
app.config.from_object(Config)
db.init_app(app)
admin.init_app(app)
from YourApp.main.routes import main
app.register_blueprint(main)
from YourApp.adminbp.routes import adminbp, user_datastore
app.register_blueprint(adminbp)
security = Security(app, user_datastore)
return app
#YourApp/adminbp/routes.py
from flask import render_template, Blueprint
from YourApp.models import User, Role
from YourApp import db, admin
from flask_admin.contrib.sqla import ModelView
from wtforms.fields import PasswordField
from flask_admin.contrib.fileadmin import FileAdmin
import os.path as op
from flask_security import current_user, login_required, RoleMixin, Security,
SQLAlchemyUserDatastore, UserMixin, utils
adminbp = Blueprint('adminbp', name)
admin.add_view(ModelView(User, db.session, category="Team"))
admin.add_view(ModelView(Role, db.session, category="Team"))
path = op.join(op.dirname(file), 'tuozhan')
admin.add_view(FileAdmin(path, '/static/tuozhan/', name='File Explore'))