Python Flask Application Context, how to expose app to other modules - python

I have a python script which uses Flask-SqlAlchemy to access a Postgres database. However, whenever I try to query the database I receive a "working out of context" error. I figured the way to do this was to wrap it in app.app_context:
import psycopg2
import json
from simple_chalk import redBright
from ...models.bin import Bin
from ...models.station import Station
from ... import db
from datetime import datetime as dt
from ... import current_app as app
def findPositionBin(stationId, find_position):
try:
with app.app_context():
result = Bin.query.filter_by(station_id=stationId).filter_by(position=find_position).first()
print("result")
return result
except Exception as e:
print(redBright(e))
However, to do so I would need to import app. The problem is that my root init.py has the app contained in a function to be called by wsgi.py to run the program.
init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_socketio import SocketIO
from flask_cors import CORS
import eventlet
import threading
db = SQLAlchemy()
migrate = Migrate()
socketio = SocketIO()
def create_app():
app = Flask(__name__, instance_relative_config=False)
CORS(app)
app.config.from_object('config.Config')
eventlet.monkey_patch()
socketio.init_app(app, cors_allowed_origins='*', async_mode='eventlet')
migrate.init_app(app, db)
with app.app_context():
from . import routes
from . import wsroutes
from .models import user, bin, ip_port, station
from .blueprints import user
from .blueprints.CubeStation import station_routes
from.database.CubeStation import station_database
from .server import startServer
from .blueprints.CubeStation.station_functions import broadcastLoop
# from .database.CubeStation import station_database
db.init_app(app)
app.register_blueprint(user.user_blueprint)
app.register_blueprint(station_routes.station_blueprint)
# app.register_blueprint(station_database.database_blueprint)
x = threading.Thread(target=startServer)
x.start()
t = threading.Thread(target=broadcastLoop)
t.start()
db.create_all()
return app
Would anyone happen to know how I can expose the app so it can be imported by other modules? Or if there is a better approach to this. Thanks in advance

Based here and here, maybe something in this way would work:
from init import app
def app_context():
with app.app_context():
yield
def findPositionBin(stationId, find_position, app_context):
try:
with app.app_context():
result = Bin.query.filter_by(station_id=stationId).filter_by(position=find_position).first()
print("result")
return result
except Exception as e:
print(redBright(e))

Related

Passing python objects from main flask app to blueprints

I am trying to define a mongodb object inside main flask app. And I want to send that object to one of the blueprints that I created. I may have to create more database objects in main app and import them in different blueprints. I tried to do it this way.
from flask import Flask, render_template
import pymongo
from admin_component.bp1 import bp_1
def init_db1():
try:
mongo = pymongo.MongoClient(
host='mongodb+srv://<username>:<passwrd>#cluster0.bslkwxdx.mongodb.net/?retryWrites=true&w=majority',
serverSelectionTimeoutMS = 1000
)
db1 = mongo.test_db1.test_collection1
mongo.server_info() #this is the line that triggers exception.
return db1
except:
print('Cannot connect to db!!')
app = Flask(__name__)
app.register_blueprint(bp_1, url_prefix='/admin') #only if we see /admin in url we gonna extend things in bp_1
with app.app_context():
db1 = init_db1()
#app.route('/')
def test():
return '<h1>This is a Test</h1>'
if __name__ == '__main__':
app.run(port=10001, debug=True)
And this is the blueprint and I tried to import the init_db1 using current_app.
from flask import Blueprint, render_template, Response, request, current_app
import pymongo
from bson.objectid import ObjectId
import json
bp_1 = Blueprint('bp1', __name__, static_folder='static', template_folder='templates')
print(current_app.config)
db = current_app.config['db1']
But it gives this error without specifying more details into deep.
raise RuntimeError(unbound_message) from None
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context(). See the documentation for more information.
Can someone point out what am I doing wrong here??
The idea you are attempting is correct; however it just needs to be done a little differently.
First, start by declaring your mongo object in your application factory:
In your app/__init__.py:
import pymongo
from flask import Flask
mongo = pymongo.MongoClient(
host='mongodb+srv://<username>:<passwrd>#cluster0.bslkwxdx.mongodb.net/?retryWrites=true&w=majority',
serverSelectionTimeoutMS = 1000
)
# Mongo is declared outside of function
def create_app(app):
app = Flask(__name__)
return app
And then in your other blueprint, you would call:
from app import mongo # This right here will get you the mongo object
from flask import Blueprint
bp_1 = Blueprint('bp1', __name__, static_folder='static', template_folder='templates')
db = mongo

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 circular dependency

I am developing a Flask application. It is still relatively small. I had only one app.py file, but because I needed to do database migrations, I divided it into 3 using this guide:
https://realpython.com/blog/python/flask-by-example-part-2-postgres-sqlalchemy-and-alembic/
However, I now can't run my application as there is a circular dependency between app and models.
app.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask import render_template, request, redirect, url_for
import os
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DB_URL']
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.debug = True
db = SQLAlchemy(app)
from models import User
... routes ...
if __name__ == "__main__":
app.run()
models.py:
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return self.username
manage.py:
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from app import app, db
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
if __name__ == "__main__":
manager.run()
They are all in the same directory. When I try to run python app.py to start the server, I receive an error which definitely shows a circular dependency (which is pretty obvious). Did I make any mistakes when following the guide or is the guide wrong? How can I refactor this to be correct?
Thanks a lot.
EDIT: Traceback
Traceback (most recent call last):
File "app.py", line 14, in <module>
from models import User
File "/../models.py", line 1, in <module>
from app import db
File "/../app.py", line 14, in <module>
from models import User
ImportError: cannot import name User
I propose the following structure:
# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
...
# app/app.py
from app.extensions import db
def create_app(config_object=ProdConfig):
app = Flask(__name__.split('.')[0])
app.config.from_object(config_object)
register_extensions(app)
...
def register_extensions(app):
db.init_app(app)
...
# manage.py
from yourapp.app import create_app
app = create_app()
app.debug = True
...
In this case, database, app, and your models are all in separate modules and there are no conflicting or circular imports.
I chased this for a few hours, landing here a few times, and it turned out I was importing my page modules (the ones holding the #app.route commands) before the line where the app was created. This is easy to do since import commands tend to be placed at the very beginning, but it doesn't work in this case.
So this:
# app/__init__.py
print("starting __init__.py")
from flask import Flask
from flask import render_template
import matplotlib.pyplot as plt
import numpy as np
import mpld3
app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config')
from . import index
from . import simple
app.run(threaded=False)
print("finished __init__.py")
Instead of having all imports on top.
Placing this here because this has to be a common error for casual flask users to encounter and they are likely to land here. I have hit it as least twice in the last couple of years.

References and URL rules in Flask

I've got a problem with my simple app in Flask. I want to write a registration page, that connect with datebase by SQLAlchemy. I've got app.py file that look like this:
import flask
import settings
import os
from flask_sqlalchemy import SQLAlchemy
# Views
from main import Main
from login import Login
from remote import Remote
from register import Register
#from models import User
#from models import Task
app = flask.Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
app.secret_key = os.urandom(24)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////database.db'
db = SQLAlchemy(app)
# Routes
app.add_url_rule('/',
view_func=Main.as_view('main'),
methods=["GET"])
app.add_url_rule('/<page>/',
view_func=Main.as_view('page'),
methods=["GET"])
app.add_url_rule('/login/',
view_func=Login.as_view('login'),
methods=["GET", "POST"])
app.add_url_rule('/remote/',
view_func=Remote.as_view('remote'),
methods=['GET', 'POST'])
app.add_url_rule('/register/',
view_func=Register.as_view('register'),
methods=['GET', 'POST'])
#app.errorhandler(404)
def page_not_found(error):
return flask.render_template('404.html'), 404
app.debug = True
app.run()
So, as you can see I have URL rules in this file and now I want to use db variable in Register view, so I need import it there, my code for this file looks like this:
import flask, flask.views
from app import db
class Register(flask.views.MethodView):
def get(self):
return flask.render_template('register.html')
def post(self):
new_user = User(username = request.form['username'],
password = request.form['passwd']
db.session.add(new_user)
db.session.commit()
return flask.redirect(flask.url_for('index'))
In that case I get error, cause I have "tied" references, in app file is:
from register import Register
but in Register file is
from app import db
So it, obviously can't work, my solutions is to add URL rule in Register file. But I don't know how. Could you anyone help me?
Sorry for my confusing title, but I just getting started with Flask and I dnon't know how to name it.
You need to move your db assignment to before the imports that expect db to exist:
import flask
import settings
import os
from flask_sqlalchemy import SQLAlchemy
app = flask.Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
app.secret_key = os.urandom(24)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////database.db'
db = SQLAlchemy(app)
# Views
# These can now use
# from app import db
# because that line has executed above
from main import Main
from login import Login
from remote import Remote
from register import Register
#from models import User
#from models import Task
Now when you import the register module and the from app import db line runs, the db variable actually exists in the app module.
Note that you cannot now use app.py as the main script; if you use python app.py then it'll be imported as the __main__ module instead of app module, and using from app import db will create a new module, separate from the main script.
Either use a separate serving script, like serve.py and move your app.run() call into that file:
from app import app
app.debug = True
app.run()
or use from __main__ import db.
You also are missing a closing ) in your register module:
new_user = User(username = request.form['username'],
password = request.form['passwd']
# ^ ^

Cannot access db when running code from Interactive Console

# main.py
from flask import Flask, jsonify
from flask.ext.cors import CORS
from shared.database import db
from src import controllers
import os
app = Flask(__name__)
cors = CORS(app, allow_headers='Content-Type')
app.register_blueprint(controllers.api)
if (os.getenv('SERVER_SOFTWARE') and os.getenv('SERVER_SOFTWARE').startswith('Google App Engine/')):
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+gaerdbms:///gaiapro_api_dev?instance=dev-gaiapro-api:gaiapro-sql'
else:
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqldb://root#127.0.0.1/gaiapro_api_dev'
app.config['DEBUG'] = True
db.init_app(app)
# shared/database.py
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
# src/controllers/__init__.py
from flask import Blueprint, jsonify, request
from src import models
from src import views
from shared.helpers import *
api = Blueprint('api', __name__)
#api.route('/test')
def test():
...
# shared/helpers.py
from flask import jsonify, request, abort, make_response
from shared.database import db
def some_method():
...
db.session.commit() # Can access db normally from here
# src/models/__init__.py
from shared.database import db
class Client(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
I am developing for GAE (Google App Engine). Basically what I want is to test my models in the Interactive Console from inside the Admin Server of _dev_appserver.py_.
I have tried to run the following code from the Interactive Console:
from main import *
from src import models
print models.Client.query.get(1)
The result was:
RuntimeError: application not registered on db instance and no application bound to current context
If I try just to print the db variable in this context, the result is: SQLAlchemy engine=None
I don't know what I am doing wrong. My code runs normally on the browser, but I cannot get it to work from the Interactive Console.
You need to be in an app context (or a request context) to access application bound objects.
An easy way to achieve this is to use Flask-Script, which provides a shell command that sets up the application for you. Or use Flask's integration with Click if you are using the development version.
To just get it working immediately, set up the context yourself:
ctx = app.app_context()
ctx.push()
# do stuff
ctx.pop()
# quit
You can also use a context in a with block:
with app.app_context():
# do stuff
Use flask shell instead of default python interpreter
$ flask shell
$ from yourappname import db
$ db # this will print your connection string
$ db.create_all()

Categories

Resources