Suppose I have something like this in app/models.py:
from flask import current_app as app
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.sql import func
db = SQLAlchemy()
class LoginLink(db.Model):
...
expiration_date = db.Column(
db.DateTime(timezone=True), nullable=False,
server_default=func.now() + str(app.config["LOGIN_LINK_EXP_TIME"]) # Error here!!!
)
And this in app/__init__.py:
from flask import Flask
from config import CONFIG_OBJECT
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(CONFIG_OBJECT[config_name])
from app.models import db
db.init_app(app)
db.create_all(app=app)
return app
Finally, this is my config.py:
from datetime import timedelta
CONFIG_OBJECT = {
"dev": "config.DevConfig",
"prod": "config.ProdConfig"
}
class Config:
...
class DevConfig(Config):
LOGIN_LINK_EXP_TIME = timedelta(seconds=30)
class ProdConfig(Config):
LOGIN_LINK_EXP_TIME = timedelta(minutes=30)
I tried to use app.app_context() everywhere (believe me) and I'm still getting this error:
RuntimeError: Working outside of application context.
I'm just trying to do the following: in a development environment I want the login links to expire in 30 seconds (for testing and demonstration purposes), but login links will last 30 minutes in a production environment.
How to accomplish this using different config environments?
Note: this is intended to be a generic question.
I think I had a misconception about application factories. We have the following from the Flask documentation:
The downside is that you cannot use the application object in the blueprints at import time. You can however use it from within a request.
Additionally:
It’s preferable to create your extensions and app factories so that the extension object does not initially get bound to the application.
What is in bold is what I was doing wrong: using the application object outside a request and bounding the extension to the application.
Therefore, I only see two solutions:
Use the app.config object (or dict) only within requests (the best IMHO).
Don't include configs that require to be used outside of requests in the app.config object (although this may complicate testing a bit).
I am trying to integrate SQLAlchemy into my application. I currently use application factories. my app/__init__.py looks like below
from flask import Flask
def create_app(config_filename):
app = Flask(__name__)
app.config.from_pyfile(config_filename)
from app.views.admin import admin
app.register_blueprint(frontend)
return app
Now I want to use sqlalchemy but the sqlalchemy object needs an instance of my app to be properly constructed. This documentation http://flask.pocoo.org/docs/1.0/patterns/appfactories/, recommends that I create this object in a separate file like say model.py
db = SQLAlchemy()
Then inside my factory method create_app in __init__.py, I add this
from app.model import db
db.init_app(app)
Now my question is, if I import db in other files, will I be importing a db that has been initialized with an instance of my app or just what is declared in the model.py file.
I want to access some extensions registered on my Flask app. I tried using app.extensions, but some of the extensions that I initialized aren't in the dict.
from flask import current_app
current_app.extensions.get(plugin_name)
extensions.py:
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_mail import Mail
from flask_debugtoolbar import DebugToolbarExtension
from flask_images import Images
from flask_redis import FlaskRedis
from flask_plugins import PluginManager
db = SQLAlchemy()
migrate = Migrate()
mail = Mail()
toolbar = DebugToolbarExtension()
images = Images()
redis = FlaskRedis()
plugin_manager = PluginManager()
main.py:
def configure_extensions(app):
db.init_app(app)
migrate.init_app(app, db)
mail.init_app(app)
toolbar.init_app(app)
images.init_app(app)
redis.init_app(app)
plugin_manager.init_app(
app,
plugin_folder="app/plugins",
plugin_import_path='app.plugins',
)
When I look at current_app.extensions, I only see some of the extensions. plugin_manager and toolbar are not in the list.
>>> for key in app.extensions: print(key)
migrate
mail
images
sqlalchemy
redis
Why aren't some extensions registered in app.extensions? Am I initializing them incorrectly? How can I get the extensions?
Extensions aren't required to add themselves to app.extensions, it's provided as an optional convenience. Apparently, those extensions don't add themselves. If you want that, consider submitting a patch to the relevant projects.
You don't need to use app.extensions to access the extension instances. Instead, import the instances you created, such as from myapp.extensions import db. That's the point of being able to create them separately from initializing them. When used in an app context (such as during a request), the objects will know what app to use.
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.
I want to structure my Flask app something like:
./site.py
./apps/members/__init__.py
./apps/members/models.py
apps.members is a Flask Blueprint.
Now, in order to create the model classes I need to have a hold of the app, something like:
# apps.members.models
from flask import current_app
from flaskext.sqlalchemy import SQLAlchemy
db = SQLAlchemy(current_app)
class Member(db.Model):
# fields here
pass
But if I try and import that model into my Blueprint app, I get the dreaded RuntimeError: working outside of request context. How can I get a hold of my app correctly here? Relative imports might work but they're pretty ugly and have their own context issues, e.g:
from ...site import app
# ValueError: Attempted relative import beyond toplevel package
The flask_sqlalchemy module does not have to be initialized with the app right away - you can do this instead:
# apps.members.models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Member(db.Model):
# fields here
pass
And then in your application setup you can call init_app:
# apps.application.py
from flask import Flask
from apps.members.models import db
app = Flask(__name__)
# later on
db.init_app(app)
This way you can avoid cyclical imports.
This pattern does not necessitate the you place all of your models in one file. Simply import the db variable into each of your model modules.
Example
# apps.shared.models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
# apps.members.models
from apps.shared.models import db
class Member(db.Model):
# TODO: Implement this.
pass
# apps.reporting.members
from flask import render_template
from apps.members.models import Member
def report_on_members():
# TODO: Actually use arguments
members = Member.filter(1==1).all()
return render_template("report.html", members=members)
# apps.reporting.routes
from flask import Blueprint
from apps.reporting.members import report_on_members
reporting = Blueprint("reporting", __name__)
reporting.route("/member-report", methods=["GET","POST"])(report_on_members)
# apps.application
from flask import Flask
from apps.shared import db
from apps.reporting.routes import reporting
app = Flask(__name__)
db.init_app(app)
app.register_blueprint(reporting)
Note: this is a sketch of some of the power this gives you - there is obviously quite a bit more that you can do to make development even easier (using a create_app pattern, auto-registering blueprints in certain folders, etc.)
an original app.py: https://flask-sqlalchemy.palletsprojects.com/en/2.x/quickstart/
...
app = flask.Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = flask.ext.sqlalchemy.SQLAlchemy(app)
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
class Computer(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
# Create the database tables.
db.create_all()
...
# start the flask loop
app.run()
I just splitted one app.py to app.py and model.py without using Blueprint. In that case, the above answer dosen't work. A line code is needed to work.
before:
db.init_app(app)
after:
db.app = app
db.init_app(app)
And, the following link is very useful.
http://piotr.banaszkiewicz.org/blog/2012/06/29/flask-sqlalchemy-init_app/