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'))
Related
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 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.
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.
I am testing a Flask application and am receiving a "working outside of application context" error. My file directory is as follows:
api
app.py
__init__.py
models
__init__.py
user.py
resources
__init__.py
deals.py
stores.py
common
__init__.py
calculations.py
decorators.py
My app.py file looks like the following:
import os
from flask import Flask, jsonify, url_for, redirect, request, g, current_app
from flask_pymongo import PyMongo
from flask_restful import Api, Resource
from flask_httpauth import HTTPTokenAuth
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.httpauth import HTTPBasicAuth
from resources.deals import Deals
from resources.stores import Stores
from models.user import User
USERDBFILE=os.path.join(os.path.join(os.path.dirname(os.path.realpath(__file__)),'database'),'db.sqlite')
#Deals database
app = Flask(__name__)
app.config["MONGO_DBNAME"] = "database"
mongo = PyMongo(app,config_prefix='MONGO')
app.db = mongo
#User database
app.config['SECRET_KEY'] = 'SECRET KEY'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.dbuser = SQLAlchemy(app)
#App url
app.APP_URL = "http://127.0.0.1:5000"
#Setup authorization
auth = HTTPTokenAuth(scheme='Token')
#Setup the app
api = Api(app)
api.add_resource(Deals, '/deals', '/Deals/<string:type>/<string:id>',endpoint="dealType")
api.add_resource(Stores, '/stores', '/Stores/<string:type>/<string:id>',endpoint="type")
if __name__ == "__main__":
if not os.path.exists(USERDBFILE):
app.dbuser.create_all()
app.run(debug=True)
My users.py file is as follows:
from flask import current_app
import os
from flask import Flask, abort, request, jsonify, g, url_for
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.httpauth import HTTPBasicAuth
from passlib.apps import custom_app_context as pwd_context
from itsdangerous import (TimedJSONWebSignatureSerializer
as Serializer, BadSignature, SignatureExpired)
class User(current_app.dbuser.Model):
__tablename__ = 'user_api'
id = current_app.dbuser.Column(current_app.dbuser.Integer,primary_key=True)
date_created = current_app.dbuser.Column(current_app.dbuser.DateTime,default=current_app.dbuser.func.current_timestamp())
date_modified = current_app.dbuser.Column(current_app.dbuser.DateTime,default=current_app.dbuser.func.current_timestamp(),
onupdate=current_app.dbuser.func.current_timestamp())
# User Name
name = current_app.dbuser.Column(current_app.dbuser.String(128),nullable=False)
# Identification Data: email & password
email = current_app.dbuser.Column(current_app.dbuser.String(128),nullable=False,unique=True)
password = current_app.dbuser.Column(current_app.dbuser.String(192),nullable=False)
company = current_app.dbuser.Column(current_app.dbuser.String(128),nullable=False,unique=True)
# Authorization Data: role & status
role = current_app.dbuser.Column(current_app.dbuser.String(32),nullable=False,default='user')
status = current_app.dbuser.Column(current_app.dbuser.Boolean,nullable=False,default=True)
hourly_limit = current_app.dbuser.Column(current_app.dbuser.Integer,nullable=False,default=100)
daily_limit = current_app.dbuser.Column(current_app.dbuser.Integer,nullable=False,default=2400)
monthly_limit = current_app.dbuser.Column(current_app.dbuser.Integer,nullable=False,default=2400)
admin = current_app.dbuser.Column(current_app.dbuser.Boolean,nullable=False,default=True)
def hash_password(self, password):
self.password_hash = pwd_context.encrypt(password)
def verify_password(self, password):
return pwd_context.verify(password, self.password_hash)
def generate_auth_token(self, expiration=600):
s = Serializer(current_app.config['SECRET_KEY'], expires_in=expiration)
return s.dumps({'id': self.id})
#staticmethod
def verify_auth_token(token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
except SignatureExpired:
return None # valid token, but expired
except BadSignature:
return None # invalid token
user = User.query.get(data['id'])
return user
I run the file in the same directory as app.py using
python app.py
But it returns the following error:
File "app.py", line 13, in <module>
from models.user import User
File "/Users/toby/api/api/models/user.py", line 10, in <module>
class User(current_app.dbuser.Model):
File "/Users/toby/api/venv/lib/python3.4/site-packages/werkzeug/local.py", line 343, in __getattr__
return getattr(self._get_current_object(), name)
File "/Users/toby/api/venv/lib/python3.4/site-packages/werkzeug/local.py", line 302, in _get_current_object
return self.__local()
File "/Users/toby/api/venv/lib/python3.4/site-packages/flask/globals.py", line 34, in _find_app
raise RuntimeError('working outside of application context')
RuntimeError: working outside of application context
If I move the contents of the user.py file into the app.py file and change the inheritance from current_app.dbuser.Model to app.dbuser.Model it seems to work fine. Does anyone know what I am doing wrong?
Flask-Sqlalchemy binds some sqlalchemy concepts like the session, the engine, and the declaritive base to the flask app. This is convenient because you only have one thing to instantiate at your uwsgi entry point (the app object) but a pain when testing- because you have to instantiate the app object.
EDIT- I am leaving the part about testing below, but I reread your question and realized you're not actually trying to test anything.
You don't have access to the 'current_app' object at import time (when you are trying to initialize your sqlalchemy models). Instead, you have to actually import the app object from your app file. This of course means you have to worry about circular dependencies...
I have a method called 'register_routes' that gets called after I initialize the app object that imports models and views files that require access to the app object at import time.
#at the bottom of app.py
def register_models(app):
from models import User
register_models(app)
# in models.py
from app import app
class User(app.dbuser.Model):
...
EDIT- the below discusses this issue with respect to unit testing
Flask-Testing is a project that attempts to solve these problems, and is almost certainly appropriate for a beginner in this area- it provides a test class to inherit from that will set up your flask app before test cases and tear it down after. (As you come to understand the various globals and what they do you may want to move away from this... but it is very helpful for getting started!)
If you don't want to do that, you need to create an app and initialize an app context before doing anything with your flask-sqlalchemy models. This may just be
app = myapp.create()
with app.test_request_context():
# do some testing...
You will probably want to refresh this in between methods, otherwise global state will leak between test cases.
Basically, flask uses quite a lot of global variables like current_app, request etc. which only exist when a flask app is instantiated and running and in various states.
You have used current_app in the definition of the User object which is evaluated as soon as the file is imported by Python. You need to ensure you only use values like this when an app is already running.
You could move the instantiation of the User class until after the app exists, but I think the root problem is why are you using current_app.dbuser.Boolean rather than say sqlalchemy.types.Boolean?
I'm not a great expert on flask.ext.sqlalchemy, but my guess is you don't need to load those definitions of things like Column and Boolean from the particular instance of the app you have. Using the static definitions from sqlalchemy would prevent you from having a dependency from the User class to the app.
Recently, I've started building an app with Flask. It's pretty easy to use, yet powerful and fun to use. Unfortunately, I'm having problems understanding how the app context works. I know about current_app, app_context and that current_app references are supposed to be used inside requests, but that's probably part of my problem.
I'm building an app with the following folder structure:
app/
main/
__init__.py
error.py
forms.py
routes.py
static/
templates/
__init__.py
email.py
models.py
config.py
run.py
I'm doing an from flask import current_app in routes.py to import config settings and it works as expected.
But when I import current_app in forms.py I can do whatever I want, but I always get this error:
Traceback (most recent call last):
File "./run.py", line 6, in <module>
app = create_app('development')
File "/home/letmecode/Projects/python/flask/folder/app/__init__.py", line 34, in create_app
from main import main as main_blueprint
File "/home/letmecode/Projects/python/flask/folder/app/main/__init__.py", line 5, in <module>
from . import routes, errors, forms
File "/home/letmecode/Projects/python/flask/folder/app/main/routes.py", line 8, in <module>
from .forms import (ChangePasswordForm, ChangeEmailForm, CreateItemForm, RetrievePasswordForm, LoginForm,
File "/home/letmecode/Projects/python/flask/folder/app/main/forms.py", line 132, in <module>
class CreateItemForm(Form):
File "/home/letmecode/Projects/python/flask/folder/app/main/forms.py", line 133, in CreateItemForm
print current_app
File "/home/letmecode/.virtualenvs/folder/local/lib/python2.7/site-packages/werkzeug/local.py", line 362, in <lambda>
__str__ = lambda x: str(x._get_current_object())
File "/home/letmecode/.virtualenvs/folder/local/lib/python2.7/site-packages/werkzeug/local.py", line 302, in _get_current_object
return self.__local()
File "/home/letmecode/.virtualenvs/folder/local/lib/python2.7/site-packages/flask/globals.py", line 34, in _find_app
raise RuntimeError('working outside of application context')
RuntimeError: working outside of application context
forms.py is just a module that contains flask-wtf form classes and validator functions.
My problem is, that I want to reference the app context inside there, in order to get settings that are used throughout the app.
What am I doing wrong here? I guess what happens inside forms.py is not part of a request and therefore fails somehow. But how else am I supposed to reference the app context, then?
Edit:
#forms.py
from flask import current_app
#[...]
class CreateItemForm(Form):
title = TextField('Title', [
validators.Length(min=3, max=140),
validators.Regexp('^[a-zA-Z][a-zA-Z0-9 ]+$', 0, 'Item titles must start with a letter and consist of numbers and letters, only')
])
description = TextAreaField('Description', [
validators.Length(min=3, max=1000),
])
tags = TextField('Tags')
branch = SelectField(
'Branch',
choices=current_app.config['BRANCHES']
)
The underlying issue is that CreateItemForm and all of the attributes are created when the forms module is imported for the first time. That means that the branch field is created whenever import .form is run, and therefore current_app is accessed then:
# app/main/routes.py
from .forms import ( ..., CreateItemForm, ...)
Looking at your stack trace, this happens in your create_app call - most likely, your create_app looks something like this:
def create_app(config_name):
app = Flask('some-name-here')
app.config.from_SOMETHING(config_name)
# Register things here
from main import main as main_blueprint
app.register_blueprint(...)
return app
At # Register things here you can simply create an app_context and import everything inside of that:
# Register things here
# Creating an explicit application context to allow
# easy access to current_app.config, etc.
with app.app_context():
from main import main as main_blueprint
app.register_blueprint(...)
return app
I found that I was being an idiot. You don't have to provide choices per form model. You can (and should) provide them via the view/route function e.g.:
form.select_field.choices = [('value1', 'label1'), ('value2', 'label2')]
This doesn't explicitly answer my question, but renders it somewhat obsolete. However, I won't accept my own answer as a solution (yet), because I am curious as to whether it's at all possible to import configuration options from via app/current_app in this case, without getting hacky. If my curiosity won't be satisfied, I'll accept my own answer.
I wish the wtforms/flask-wtf documentation would focus less on the obvious and more on the not so obvious.