access app config variables in Flask app - python

I have a flask app that looks like this:
myapp
-- application.py
-- models.py
-- queries.py
-- routes.py
-- settings.py
application.py looks like this:
from flask import Flask
from myapp import routes
def create_app(config_object):
app = Flask(__name__)
app.config.from_object(config_object)
app.register_blueprint(routes.main)
return app
queries.py looks like this
from myapp.models import User
class QueryConnection(DATABASE_URI):
self.engine = create_engine(DATABASE_URI)
self.session = sessionmaker(bind=self.engine)
self.Session = self.session()
def get_all_users(self):
return self.Session.query(User).all()
routes.py looks like this:
from flask import current_app, Blueprint, render_template
from myapp import queries
main = Blueprint('main', __name__, url_prefix='/')
query_connection = queries.QueryConnection(current_app.config['DATABASE_URI'])
#main.route("/")
def index():
return render_template('index.jinja2', list=query_connection.get_all_users())
models.py looks like this:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
I want to get the current_app.config['DATABASE_URI'] in routes.py so I can query the database but I don't know how to pass the app context from application.py to queries.py. I tried using this in application.py
with app.app_context():
app.register_blueprint(main)
But I get the error "working outside of application context". Anything else gives the same error.
I want to encapsulate the queries in its own class so that I can pass the database uri in different contexts like in tests.

you need to import the file, from where you are calling the create_app method. My guess is that it is the application.py.
--- D_file.py
app = create_app(your_config)
--- queries.py
from D_file import app
DB_URI = app.config['DATABASE_URI']

Why are you involving the Flask application at all in the SQLAlchemy setup? You have a configuration object available to you, use that directly. Then you can attach a session to the flask global object, or create a session dynamically.
In queries.py
import get_config from config
config_object = get_config()
engine = create_engine(config_object['DATABASE_URI'])
session_factory = sessionmaker(bind=engine)
Session = scoped_session(session_factory)
def get_all_users():
session = Session()
users = session.query(User).all()
session.close()
return users
And in your app factory:
from flask import Flask, g
from myapp import routes
from myapp.queries import Session
def create_app(config_object):
app = Flask(__name__)
app.config.from_object(config_object)
app.register_blueprint(routes.main)
# automatically set up and close a session for each request
#app.before_request
def before_request(response):
g.session = Session()
#app.after_request
def after_request(response):
g.session.close()
return app

Related

How to create table from model classes from another directory in sqlalchemy?

This is my flask project directory structure,
src/models-> UserModel.py
-> PassengerModel.py
src/run.py
and this run.py file contains the database connection object,
from flask import Flask
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from flask_jwt_extended import JWTManager
from flask_restful import Resource
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///appDB.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'some-secret-string'
app.config['JWT_SECRET_KEY'] = 'jwt-secret-string'
app.config['JWT_BLACKLIST_ENABLED'] = True
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access', 'refresh']
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = False
CORS(app)
db = SQLAlchemy(app)
jwt = JWTManager(app)
db.create_all()
#app.before_first_request
def create_tables():
db.create_all()
api = Api(app)
from src.models import UserModel, PassengerModel, DriverModel, OwnerModel,
VehicleModel, TripPlanModel, TripStatusModel,PickupLocationsModel,
WaypointsModel, DriverFeedbackModel, PassengerFeedbackModel
once the all these models inside one file, db.create_all() method works fine and creates the database file.
But when all the model classes inside the models directory and even after imports it to run.py file db.create_all() method is not working.
How can I create DB from models in a different directory?
Thanks in advance.
try this:
File structure:
src/models -> UserModel.py
-> PassengerModel.py
-> __init__.py
/__init__.py
src/run.py
Import models:
try:
from src.models.usermodel import User
from src.models.passengermodel import Passenger
print('Models imported')
except ImportError as e:
print(e)
Hope that helps you. Cheers
PS: Probably you will need to put the imports for the models at the bottom of your file before the following:
Imports ...
...
your code
...
*** models imports here ***
if __name__ == '__main__':
app.run()

flask-migrate issue while refactoring code

I got the below file structure for a Python-Flask app with flask-migrate :
My issues are
1-I'm unable to use db and create_app inside manage.py
When I do:
$ python manage.py db init
I got below error:
File "/app/main/model/model.py", line 25, in <module>
class User(db.Model):
NameError: name 'db' is not defined
(db is defined in main.init.py )
I have tried different options with no success.
I want to keep the manage.py , model.py and main.init.py in separate files.
2- In model .py I will need db .How will I make db available to model.py ?
Here below is manage.py
# This file take care of the migrations
# in model.py we have our tables
import os
import unittest
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from app.main import create_app
from app.main import db
# # We import the tables into the migrate tool
from app.main.model import model
app = create_app(os.getenv('BOILERPLATE_ENV') or 'dev')
app.app_context().push()
manager = Manager(app)
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
#### If I add model.py here all should be easier , but still I have the
#### issue with
#### from app.main import create_app , db
#manager.command
def run():
app.run()
#manager.command
def test():
"""Runs the unit tests."""
tests = unittest.TestLoader().discover('app/test', pattern='test*.py')
result = unittest.TextTestRunner(verbosity=2).run(tests)
if result.wasSuccessful():
return 0
return 1
if __name__ == '__main__':
manager.run()
This is app.init.py where db and create_app are defined
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_cors import CORS
from .config import config_by_name
from flask_restful import Resource, Api
# from flask_restplus import Resource
from app.main.controller.api_controller import gconnect, \
showLogin, createNewTest, getTest, getTests, getIssue, createNewIssue
db = SQLAlchemy()
flask_bcrypt = Bcrypt()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config_by_name[config_name])
cors = CORS(app,
supports_credentials=True,
resources={r"/api/*":
{"origins":
["http://localhost:3000",
"http://127.0.0.1:3000"]}})
api = Api(app)
db.init_app(app)
flask_bcrypt.init_app(app)
api.add_resource(gconnect, '/api/gconnect')
api.add_resource(showLogin, '/login')
api.add_resource(createNewTest, '/api/test')
api.add_resource(getTest, '/api/test/<int:test_id>')
api.add_resource(getTests, '/api/tests')
api.add_resource(getIssue, '/api/issue/<int:issue_id>')
api.add_resource(createNewIssue, '/api/issue')
return app
And this is (just one of the table for simplicity) of my model
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
from sqlalchemy import create_engine
from sqlalchemy.sql import func
# # # This will let sql alchemy know that these clasess
# # # are special Alchemy classes
# Base = declarative_base()
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
email = db.Column(db.String(250), nullable=False)
pictures = db.Column(db.String(250))
role = db.Column(db.String(25), nullable=True)
My issues are:
1-I'm unable to use db and create_app inside manage.py
When I do:
$ python manage.py db init
I got below error:
File "/app/main/model/model.py", line 25, in
class User(db.Model):
NameError: name 'db' is not defined
(db is defined in main.init.py )
I have tried different options with no success.
I want to keep the manage.py , model.py and main.init.py in separate files.
2- In model .py I will need db .How will I make db available to model.py ?
A simple solution is to create a seperate initializtions file besides your __init__.py. e.g. init.py where you initialize sqlalchemy along with other extensions. That way they can be imported in all the modules without any circular dependencies problems.
A more elegant solution however is to you use Flask's current_app and g proxies. They were made to help Flask users circumvent any problems with circular dependencies.
Typically you initalize the flask app in the __init__.py module and the __init__.py module sometimes has to import some variables from its sub-modules. This becomes problematic when sub-modules try to import initalized extensions
As a general rule of thumb, outer modules should be importing from their submodules not the other way around.
So here's one way you can solve your problem (cited from here):
** __init__.py
from flask import g
def get_db():
if 'db' not in g:
g.db = connect_to_database()
return g.db
#app.teardown_appcontext
def teardown_db():
db = g.pop('db', None)
if db is not None:
db.close()
def init_db():
db = get_db()
Now you can easily import your db connection into any other module by:
from flask import g
db = g.db
db.do_something()

flask_sqlalchemy create_all() fails silently in unit testing

I'm writing unit tests for a REST API written in Flask with the flask_sqlalchemy extension. Because I have a number of model classes, I wrote a TestCase subclass to do the standard setUp/cleanUp of the test database. All my test classes inherit from this. Each test succeeds when run alone, but when I run more than one test in a single class, the second setUp() fails on the self.db.session.commit() (I'm trying to add an entry to the User table) because self.db.create_all() has (silently) failed to create any tables.
Here is my base test class, in the __init__.py of the test package:
import unittest
from .test_client import TestClient
from .. import create_app
from pdb import set_trace as DBG
class ApiTest(unittest.TestCase):
default_username = 'fred'
default_password = 'bloggs'
db = None
def setUp(self):
try:
self.app = create_app('testing')
self.addCleanup(self.cleanUp)
self.ctx = self.app.app_context()
self.ctx.push()
from .. import db
self.db = db
self.db.session.commit()
self.db.drop_all(app=self.app)
from ..models import User, Player, Team, Match, Game
# self.app.logger.debug('drop_all())')
self.db.create_all(app=self.app)
# self.app.logger.debug('create_all())')
user = User(user_name=self.default_username)
user.password = self.default_password
self.db.session.add(u)
self.db.session.commit()
self.client = TestClient(self.app, user.generate_auth_token(), '')
except Exception, ex:
self.app.logger.error("Error during setUp: %s" % ex)
raise
def cleanUp(self):
try:
self.db.session.commit()
self.db.session.remove()
self.db.drop_all(app=self.app)
# self.app.logger.debug('drop_all())')
self.ctx.pop()
except Exception, ex:
self.app.logger.error("Error during cleanUp: %s" % ex)
raise
Can anyone tell me what's wrong here please?
EDIT: Added the code for create_app() as requested.
# chessleague/__init__.py
import os
from flask import Flask, g
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from . import config
app = None
db = None # The database, initialised in create_app()
def create_app(config_name):
app = Flask(__name__)
app.config.update(config.get_config(config_name))
# if app.config['USE_TOKEN_AUTH']:
# from api.token import token as token_blueprint
# app.register_blueprint(token_blueprint, url_prefix='/auth')
import logging
from logging.handlers import SysLogHandler
syslog_handler = SysLogHandler()
syslog_handler.setLevel(logging.WARNING)
app.logger.addHandler(syslog_handler)
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
global db
db = SQLAlchemy(app)
db.init_app(app)
from .models import User,Player,Game,Match,Team,Post
db.create_all()
from .api import api as api_blueprint
app.register_blueprint(api_blueprint, url_prefix='/chessleague')
return app
`
create_all() applies to the metadata, that is being discovered by importing modules with models. In your case, models' metadata binds to the db from your models.py but you are calling create_all() from chessleague/__init__.db from create_app() function, which is different objects for SqlAlchemy. You can fix that by using db from models.py:
from .models import User,Player,Game,Match,Team,Post, db as models_db
models_db.create_all()
Here's the initialisation sequence that worked for me - comments welcome!
My test class setUp() calls create_app(config_name) from the main app package.
The main app package(__init__.py) creates the app instance at module level, ie app=Flask(my_app_package_name)
Then my function
create_app(config_name)
loads the right config into app.config (including the right SQLACHEMY_DATABASE_URL)
imports the model classes and db (as model_db) from models.py
This import creates the symbol db at module level in models.py, followed by the model class definitions:
# models.py
from . import app
db = SQLAlchemy(app)
...
class User(db.Model)
...
etc
Now everything is set up properly: the symbol 'db' can be imported anywhere from models.py, and I can call db.create_all() successfully from my test setUp().
#Fian, can you post your solution as an answer so I can give you credit?

Initialize a Flask-WhooshAlchemy index when using the app factory pattern

I am developing an app with Flask using the application factory pattern. Initializing the Whoosh index doesn't work, because current_app cannot be used without setting up an app context explicitly. How do I do this?
__init__.py
from flask import Flask
from .frontend import frontend
def create_app(configfile=None):
app = Flask(__name__)
from .models import db
db.init_app(app)
app.register_blueprint(frontend)
return app
models.py
from flask_sqlalchemy import SQLAlchemy
import flask_whooshalchemy as wa
from flask import current_app
db = SQLAlchemy()
class Institution(db.Model):
__searchable__ = ['name', 'description']
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40))
description = db.Column(db.Text)
# this does not work
wa.whoosh_index(current_app, Institution)
You can't use current_app outside an application context. An application context exists during a request, or when explicitly created with app.app_context().
Import your models during create_app and index them with Whoosh there. Remember, the factory is where you're performing all setup for the application.
def create_app():
app = Flask('myapp')
...
from myapp.models import Institution
wa.whoosh_index(app, Institution)
...
return app
If you want to keep the code local to the blueprint, you can use the blueprint's record_once function to perform the index when the blueprint is registered on the app.
#bp.record_once
def record_once(state):
wa.whoosh_index(state.app, Institution)
This will be called at most once when registering the blueprint with an app. state contains the app, so you don't need the current_app.

Flask-SQLAlchemy import/context issue

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/

Categories

Resources