Flask-SQLAlchemy application context error - python

I am trying to use SQLAlchemy not in a view function (I was doing something like this with Flask-APSheduler).
I know that there were already a lot of topics related to this theme, but none of them were helpful to me.
So, first of all I will show my code:
./run.py
from app import create_app
from flask_config import DevConfig, ProdConfig
app = create_app(DevConfig)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
./app/__init__.py
from flask import Flask
from .node import node
from .models import db
def create_app(app_config=None):
app = Flask(__name__, instance_relative_config=False)
app.config.from_object(app_config)
db.init_app(app)
app.register_blueprint(node)
return app
./app/models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Users(BaseFuncs, db.Model):
...
./app/node.py
from flask import Blueprint, request
from .bot import bot, secret
import telebot
node = Blueprint('node', __name__)
#node.route('/{}'.format(secret), methods=['POST'])
def handler():
bot.process_new_updates([telebot.types.Update.de_json(request.get_data().decode('utf-8'))])
return 'ok', 200
./app/bot.py
from flask import current_app as app
...
#bot.message_handler(commands=['test'])
def cmd_test(message):
with app.app_context():
print(Users.query.filter_by(id=0).first())
So when I am trying to call cmd_test from my application I am getting this error:
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 tried to use g variable and before_request methods, because every time before calling the database there is a call to the route 'handler', but this also doesn't work.
I also tried to use db.get_app(), but there was no effect.
So my question is how to call database right outside the views?

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

Access Flask-SQLAlchemy database outside of view functions

I have created a small Flask application which stores its data in an sqlite database that I access via flask-sqlalchemy.
However, when I run it, I get the following error:
RuntimeError: No application found. Either work inside a view function or push an application context. See http://flask-sqlalchemy.pocoo.org/contexts/.
I have debugged my application and now know that this error stems from these two functions:
def user_exists(email):
if User.query.filter_by(email = email).count() == 0:
return False
else:
return True
def get_user(email):
user = User.query.filter_by(email = email).first()
return user
Now I am wondering: Is it impossible to access the database via flask-sqlalchemy outside of view functions?
For further context, I added the files in which I configure my flask app:
presentio.py
from app import create_app
app = create_app(os.getenv("FLASK_CONFIG", "default"))
app/init.py
from flask_mail import Mail
from flask_sqlalchemy import SQLAlchemy
from config import config
mail = Mail()
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
mail.init_app(app)
db.init_app(app)
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix = "/auth")
from .text import text as text_blueprint
app.register_blueprint(text_blueprint, url_prefix = "/text")
return app
You need to give the flask app a context after you create it.
This is done automatically in view functions, but outside those, you need to do this after you create the app:
app.app_context().push()
See the docs: https://flask-sqlalchemy.palletsprojects.com/en/2.x/contexts/

Configure Email Token with Flask Factory Application create_app

I am getting confused with configurations and imports once I started using the Flask factory application pattern.
I am creating an application with the function create_app in #app/init.py
I have a config file for setting the development/testing/production variables, and an instance folder with another config file.
def create_app(config_name):
app=Flask(__name__, instance_relative_config=True)
app.config.from_object(app_config[config_name])
app.config.from_pyfile('config.py')
etc...
return app
I am using blueprints and have an authentication view in #app/auth/views.py
I am trying to set up email confirmation tokens using URLSafeTimedSerializer...
from itsdangerous import URLSafeTimedSerializer
#auth.route('/register', methods=['GET','POST'])
def register():
ts = URLSafeTimedSerializer(app.config['SECRET_KEY'])
token = ts.dumps(self.email, salt='email-confirm-key')
etc...
Now my problem is, my variable 'ts' needs the app.config['SECRET_KEY'] set. But I am unable to define the app variable (as is shown in all online tutorials). I get an error when I try to import...(in #app/auth/views.py)
from .. import app
and when I try to import like...
from .. import create_app
Can someone shine light on how to initialize modules using 'app' and app.config outside the flask app factory create_app?
Hope you understand my question.
In this scenario you should use Flask.current_app
from flask import current_app
...
ts = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
From the documentation:
flask.current_app
Points to the application handling the request. This
is useful for extensions that want to support multiple applications
running side by side. This is powered by the application context and
not by the request context, so you can change the value of this proxy
by using the app_context() method.
This link aso explains further details about the Flask application factory methodology, in particular using current_app to access the app configuration.

Can I keep all Flask blueprints in one file?

I am using an app factory to initialize my app. In it, I import all the blueprints and register them one by one. Is there a way I can move the import and register statements to a different file or inform the factory about them without referencing them all individually?
def create_app(config_filename):
app = Flask(__name__)
app.config.from_object(config_filename)
from app.users.models import db
db.init_app(app)
from app.users.views import users
from app.posts.views import posts
app.register_blueprint(posts, url_prefix='/posts')
app.register_blueprint(users, url_prefix='/users')
return app
In my project I'm actually generating the blueprints with another script, so I'd like to be able to generate the registration too by appending to a file or something, rather than trying to modify code in the factory.
Yes, you can import and register the blueprints in some other module. But there's no practical point to this approach, it just moves the imports and register calls somewhere else.
myapp/blueprints.py:
from app.users.views import users
from app.posts.views import posts
def init_app(app):
app.register_blueprint(users, prefix='/users')
app.register_blueprint(posts, prefix='/posts')
myapp/__init__.py:
def create_app():
app = Flask(__name__)
# ...
from myapp import blueprints
blueprints.init_app(app)
# ...
Something more useful might be to tell the app what packages to import from and have the app expect to find a blueprint in some standard location for each package. Assuming the blueprint variable will always have the same name as the package, is defined in views, and has the same prefix as the name:
from werkzeug.utils import import_string
def create_app():
app = Flask(__name__)
# ...
for name in ('users', 'posts'):
bp = import_string('myapp.{0}.views:{1}'.format(name, name))
app.register_blueprint(bp, prefix='/{0}'.format(name))
# ...

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