Reading the documentation I understand that flask defines a class flask.session.
The thing that confuses me is that when people use it they don't instantiate an object of the session class, but use session directly, as in the following code:
from flask import Flask, session
app = Flask(__name__)
#app.route('/')
def index():
session['key'] = 'value'
I don't understand why the code shouldn't look something like this instead:
from flask import Flask, session
app = Flask(__name__)
s = session() # so s is an instance of the flask.session class
#app.route('/')
def index():
s['key'] = 'value'
I am also wondering if this has anything to do with session being a proxy, as it says in the documentation. I read the 'Notes on Proxies' but couldn't understand much.
Awesome question.
It gets initialized in flasks globals.py
https://github.com/pallets/flask/blob/master/flask/globals.py
session = LocalProxy(partial(_lookup_req_object, 'session'))
So when you import from flask you import from its package __init__.py which pulls session from globals.py and initializes it. You grab a reference to that when you directly import it.
I should clarify that session itself is not a class. It’s an instance of the LocalProxy class, which is a proxy to the request context.
Related
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).
This question is an extension on my previous one here. I was suggested to put more to explain the problem. As the heading says, I am trying to find a way to avoid importing the application factory (create_app function) into a module that needs application context and were "import current_app as app" is not sufficient.
My problem is I have a circular import problem due to this create_app function which I need to pass in order to get the app_context.
In my __ini__.py, I have this:
# application/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_restful import Api
from application.resources.product import Product, Products
from application.resources.offer import Offer, Offers # HERE IS THE PROBLEM
api = Api()
db = SQLAlchemy()
api.add_resource(Product, "/product/<string:name>") # GET, POST, DELETE, PUT to my local database
api.add_resource(Products, "/products") # GET all products from my local database
api.add_resource(Offer, "/offer/<int:id>") # POST call to the external Offers API microservise
api.add_resource(Offers, "/offers") # GET all offers from my local database
def create_app(config_filename=None):
""" Initialize core application. """
app = Flask(__name__, instance_relative_config=False)
app.config.from_object("config.Config")
db.init_app(app)
api.init_app(app)
with app.app_context():
db.create_all()
return app
The problem is in this line:
from application.resources.offer import Offer, Offers # HERE IS THE PROBLEM
because in that module, I have:
#application/resources/offer.py
from flask_restful import Resource
from application.models.offer import OfferModel # IMPORTING OFFER MODEL
which in turn imports application/models/offer.py where I have the critical part:
#application/models/offer.py
import requests
# from flask import current_app as app
from application import create_app # THIS CAUSES THE CIRCULAR IMPORT ERROR
from sqlalchemy.exc import OperationalError
app = create_app() # I NEED TO CREATE THE APP IN ORDER TO GET THE APP CONTEXT BECASE IN THE CLASS I HAVE SOME FUNCTIONS THAT NEED IT
class OfferModel(db.Model):
""" Data model for offers. """
# some code to instantiate the class... + other methods..
# THIS IS ONE OF THE METHODS THAT NEED APP_CONTEXT OR ELSE IT WILL ERROR OUT
#classmethod
def update_offer_price(cls):
""" Call offers api to get new prices. This function will run in a separated thread in a scheduler. """
with app.app_context():
headers = {"Bearer": app.config["MS_API_ACCESS_TOKEN"]}
for offer_id in OfferModel.offer_ids:
offers_url = app.config["MS_API_OFFERS_BASE_URL"] + "/products/" + str(offer_id) + "/offers"
res = requests.get(offers_url, headers=headers).json()
for offer in res:
try:
OfferModel.query.filter_by(offer_id=offer["id"]).update(dict(price=offer["price"]))
db.session.commit()
except OperationalError:
print("Database does not exists.")
db.session.rollback()
I have tried to use from flask import current_app as app to get the context, it did not work. I don't know why it was not sufficient to pass current_app as app and get the context because it now forces me to pass the create_app application factory which causes the circular import problem.
Your update_offer_price method needs database interaction and an access to the configuration. It gets them from the application context but it works only if your Flask application is initialized. This method is run in a separate thread so you create the second instance of Flask application in this thread.
Alternative way is getting standalone database interaction and configuration access outside the application context.
Configuration
Configuration does not seem a problem as your application gets it from another module:
app.config.from_object("config.Config")
So you can directly import this object to your offer.py:
from config import Config
headers = {"Bearer": Config.MS_API_ACCESS_TOKEN}
Database access
To get standalone database access you need to define your models via SQLAlchemy instead of flask_sqlalchemy. It was already described in this answer but I post here the essentials. For your case it may look like this. Your base.py module:
from sqlalchemy import MetaData
from sqlalchemy.ext.declarative import declarative_base
metadata = MetaData()
Base = declarative_base(metadata=metadata)
And offer.py module:
import sqlalchemy as sa
from .base import Base
class OfferModel(Base):
id = sa.Column(sa.Integer, primary_key=True)
# Another declarations
The produced metadata object is used to initialize your flask_sqlalchemy object:
from flask_sqlalchemy import SQLAlchemy
from application.models.base import metadata
db = SQLAlchemy(metadata=metadata)
Your models can be queried outside the application context but you need to manually create database engine and sessions. For example:
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from config import Config
from application.models.offer import Offer
engine = create_engine(Config.YOUR_DATABASE_URL)
# It is recommended to create a single engine
# and use it afterwards to bind database sessions to.
# Perhaps `application.models.base` module
# is better to be used for this declaration.
def your_database_interaction():
session = Session(engine)
offers = session.query(Offer).all()
for offer in offers:
# Some update here
session.commit()
session.close()
Note that with this approach you can't use your models classes for queriing, I mean:
OfferModel.query.all() # Does not work
db.session.query(OfferModel).all() # Works
ok so this is how I solved it. I made a new file endpoints.py where I put all my Api resources
# application/endpoints.py
from application import api
from application.resources.product import Product, Products
from application.resources.offer import Offer, Offers
api.add_resource(Product, "/product/<string:name>") # GET, POST, DELETE, PUT - calls to local database
api.add_resource(Products, "/products") # GET all products from local database.
api.add_resource(Offer, "/offer/<int:id>") # POST call to the Offers API microservice.
api.add_resource(Offers, "/offers") # GET all offers from local database
Then in init.py I import it at the very bottom.
# aplication/__init__.py
from flask import Flask
from flask_restful import Api
from db import db
api = Api()
def create_app():
app = Flask(__name__, instance_relative_config=False)
app.config.from_object("config.Config")
db.init_app(app)
api.init_app(app)
with app.app_context():
from application import routes
db.create_all()
return app
from application import endpoints # importing here to avoid circular imports
It is not very pretty but it works.
spinngod.py - flask app starter code
from app import create_app
import sys
run_profile = str(sys.argv[1]) if len(sys.argv) >= 2 else 'development'
app = create_app(run_profile)
print("App Root Path:" + app.root_path)
if __name__ == '__main__':
print sys.path
app.run(debug=True, host='0.0.0.0')
app/init.py - creates flask app
def create_app(profile_name):
print "currently active profile:" + profile_name
app = Flask(__name__)
############# configurations ####################
app.config.from_object(config[profile_name])
configure_app(app)
configure_app_logger(app)
#################### blueprint registration and rest_plus namespace additions ###############
from api_1_0 import api as api_1_0_blueprint
from api_1_0.restplus import api_restplus
# ************************************************** #
api_restplus.init_app(api_1_0_blueprint)
api_restplus.add_namespace(application_namespace)
api_restplus.add_namespace(pipeline_template_namespace)
api_restplus.add_namespace(loadbalancer_namespace)
api_restplus.add_namespace(servergroup_namespace)
api_restplus.add_namespace(task_namespace)
# ************************************************** #
app.register_blueprint(api_1_0_blueprint)
##############################################################
return app
I want to access flask config variables defined in config.py in some other files which are outside application context. The app configuration depends on which profile it is started with (dev,stage or production) which is being passed from command line as an arg.
The only way that I can think of accessing config variables outside app context is to set profile (dev,stage or prod) as an environment variable and
then import directly from config file.
The second way that I tried was to move creation of flask app in app/init.py outside method.
This is how I am trying to access config variables in another class.
import requests
class Client(object):
def __init__(self):
from app import app
print "fjaijflkajsf" + app.config['SPINNAKER_BASE_URL']
pass
Is there a way better of doing this in flask ?
From the docs:
Rather than passing the application around to each function, the current_app and g proxies are accessed instead.
The Flask application object has attributes, such as config, that are useful to access within views and CLI commands. However, importing the app instance within the modules in your project is prone to circular import issues.
Flask solves this issue with the application context. Rather than referring to an app directly, you use the the current_app proxy, which points to the application handling the current activity.
You import current_app like this:
from flask import current_app
and then access the config or other attributes like this:
config = current_app.config
Example:
src/application.py (where config is set in the context)
create_app():
app = Flask('app')
app.config.from_object(some_class)
return app
src/module/another_module.py
from flask import current_app
def function_that_requires_config():
config = current_app.config
Alternative:
src/application.py (where config is set in the context)
APP = create_app(os.environ.get('FLASK_ENV'))
src/module/another_module.py
from src.application import APP
def function_that_requires_config():
config_value = APP.config.get(config_key, default_value)
Not sure if it is good to put it here as it may not respond to the question directly, but here is the cleanest way i've figured to use config values outside of requests, without having to pass config as a param.
The solution is actually pretty simple, juste consider the part of your code as a flask_extension.
my exemple will be the use of external api, with ROOT_URL in the config file, and i don't want to make api call from within my routes, so the api is in its own module.
in my create_app fuction:
from flask import Flask
from .api import api
from .configmodule import Config
from .model import db
def create_app(environment):
app = Flask(__name__)
app.config.from_object(Config.get_config(environment))
db.init_app(app)
api.init_app(app) # here i use api.init_app the same way i do for sqlalchemy
and in api/init.py
class Api:
def init_app(self, app):
self.config = app.config
api = Api()
and in any files in my api modude i can now write
from . import api
def foo():
print(api.config.get("API_ROOT_URL"))
this can even be improved if you feel the need to access some other global app vars from your module.
I am trying to get the Pyramid Web framework to handle a request using Mongo but I am a relative newbie to both. I cannot get my view to recognize a database attached to a request.
In development.ini:
###
# configure mongodb
###
mongo_uri = mongodb://localhost:27017/nomad
The __init__.py imports and main function:
# imports for Mongodb
from urllib.parse import urlparse
from gridfs import GridFS
from pymongo import MongoClient
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
init_includes(config)
init_routing(config)
db_url = urlparse(settings['mongo_uri'])
config.registry.db = MongoClient(
host=db_url.hostname,
port=db_url.port,
)
def add_db(request):
db = config.registry.db[db_url.path[1:]]
if db_url.username and db_url.password:
db.authenticate(db_url.username, db_url.password)
return db
def add_fs(request):
return GridFS(request.db)
config.add_request_method(add_db, 'db', reify=True)
config.add_request_method(add_fs, 'fs', reify=True)
config.scan()
return config.make_wsgi_app()
In jobscontroller.py, which is the handler view making the request:
import pyramid_handlers
from nomad.controllers.base_controller import BaseController
class JobsController(BaseController):
#pyramid_handlers.action(renderer='templates/jobs/index.pt')
def index(request):
all_jobs = request.db['jobs'].find()
return {'all_jobs': all_jobs}
I get an error:
all_jobs = request.db['jobs'].find()
AttributeError: 'JobsController' object has no attribute 'db'
I am using Pyramid handlers to manage routing and views, and I know that all of this works because all my routes resolve and deliver web pages. It's only the jobs controller that's funky, and only after I tried adding that request.db call.
Can someone help me understand what's going on?
You're not referring to the request - you're referring to the object itself (usually named self, but you have named it request - which would work if it was just a function and not a method on an object). Since you're inside an object of a class, the first parameter is always the object itself:
class JobsController(BaseController):
#pyramid_handlers.action(renderer='templates/jobs/index.pt')
def index(self, request):
all_jobs = request.db['jobs'].find()
return {'all_jobs': all_jobs}
How to import a function and variable from app/__init__.py and app/blueprint/__init__.py , respectively, inside app/blueprint/views.py ?
app/__init__.py
def main():
<..>
app/blueprint/__init__.py
from flask import Blueprint
blueprint = Blueprint('blueprint', __name__, template_folder='templates')
app/blueprint/views.py
import blueprint
import main
from app.__init__ import *
from app.blueprint.__init__ import *
should import all the functions and variables from both files.
However, though I don't think init file is supposed to be used for this.
Below examples of Flask Blueprints I used my project, learnt structure from Udemy tutorial, I think the idea is generally the init files are used to make a Python directory into a package so you can import stuff within it. You'd probable better create new files with the functions (less often variables) you want to import, maybe experts will confirm, but I think generally you leave Python init files blank unless you really know what you're doing.
from flask import Flask, render_template
from Source.common.database import Database
from Source.models.users.views import user_blueprint
from Source.models.street_lists.views import street_list_blueprint
# from Source.models.street_reports.views import street_report_blueprint
__author__ = "Will Croxford, with some base structure elements based on Github: jslvtr, \
from a different tutorial web application for online price scraping"
app = Flask(__name__)
app.config.from_object('Source.config')
app.secret_key = "123"
app.register_blueprint(user_blueprint, url_prefix="/users")
app.register_blueprint(street_list_blueprint, url_prefix="/streetlists")
# app.register_blueprint(street_report_blueprint, url_prefix="/streetreports")
#app.before_first_request
def init_db():
Database.initialize()
#app.route('/')
def home():
return render_template('home.jinja2')
#app.route('/about_popup.jinja2')
def info_popup():
return render_template('about_popup.jinja2')
Flask Views file example:
# In this model, views.py files are the Flask Blueprint for this object.
# ie they describe what HTTP API endpoints are associated to objects of this class.
from flask import Blueprint, render_template, request, redirect, url_for
from Source.models.street_lists.street_list import StreetList
__author__ = 'jslvtr'
street_list_blueprint = Blueprint('street_lists', __name__)
#street_list_blueprint.route('/')
def index():
prop_query = StreetList.get_from_mongo(streetpart="bum")
return render_template('street_lists/street_list.jinja2', stores=prop_query)
You can look at pocoo.org flask doc examples, and search other SO questions for Flask blueprint template examples I think. Good luck!
I read the blog suggested by Will Croxford, and here's the solution to my problem:
app/blueprint/views.py
from app import main
from app.blueprint import blueprint