I am working on a small web project using Flask/Python. This is a simple client side application without database.
I want to set the REST service address as a global attribute, but haven't figured out how to do that.
I know that attributes can be seted in flask.config like this:
app = Flask(__name__)
app.config['attribute_name'] = the_service_address
but the Blueprint module cannot access the 'app' object.
Thanks a lot for your time.
Within a request context (i.e. in a view/handler) you can access the config on the current_app
from flask import current_app
current_app.config['attribute_name']
You can do this like adding an attribute to any python object:
def create_web_app():
app = Flask('foods')
setattr(app, 'cheese', CheeseService())
Related
I am trying to use a global configuration when defining an ElasticSearch DSL model, which is more or less a regular Python class aka service class.
"""Define models"""
from elasticsearch_dsl import Document, Text
from flask import current_app
class Greeting(Document):
"""Define Greeting model"""
message = Text()
class Index:
name = current_app.config['GREETINGS_INDEX']
def save(self, ** kwargs):
return super().save(** kwargs)
Unfortunately, if my import statement is at the top of the view file, I get this error message:
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.
The only way to get things to work is if I import the model/service class inside the request like this:
from elasticsearch_dsl import Search
from flask import Blueprint, current_app
# from .models import Greeting ### this will throw the application context error
greetings = Blueprint(
'greetings',
__name__,
url_prefix='/greetings/'
)
...
#greetings.route("/elasticsearch/new/")
def new_greeting_using_elasticsearch():
from .models import Greeting ### this avoids the application context error
Greeting.init()
greeting = Greeting(message="hello, elastic")
greeting.save()
return(
"a greeting was saved; "
"it is viewable from https://localhost:5000/greetings/elasticsearch/"
)
This seems like a code smell. Is there another way to accomplish using reusing configurations that can keep the import statement at the top of the file?
These questions/answers seem to suggest that this is the only way:
How to access config value outside view function in flask
Flask - RuntimeError: Working outside of application context
Am I missing something? Should I rearchitect my application to avoid this? Or is this just a Flask-way/thing?
Thank you for your help 🙏
Other questions/answers/articles that did not help me:
"RuntimeError: Working outside of application context " with Python Flask app ( Sending gmail using scheduler )
https://flask.palletsprojects.com/en/0.12.x/appcontext/#creating-an-application-context
Access config values in Flask from other files
RuntimeError: working outside of application context
Python #property in Flask configs?
Reading properties from config file with Flask and Python
I want to get the app instance in my router file, what should I do ?
My main.py is as follows:
# ...
app = FastAPI()
app.machine_learning_model = joblib.load(some_path)
app.include_router(some_router)
# ...
Now I want to use app.machine_learning_model in some_router's file , what should I do ?
Since FastAPI is actually Starlette underneath, you could store the model on the app instance using the generic app.state attribute, as described in Starlette's documentation (see State class implementation too). Example:
app.state.ml_model = joblib.load(some_path)
As for accessing the app instance (and subsequently, the model) from outside the main file, you can use the Request object. As per Starlette's documentation, where a request is available (i.e., endpoints and middleware), the app is available on request.app. Example:
from fastapi import Request
#router.get('/')
def some_router_function(request: Request):
model = request.app.state.ml_model
In my flask application I need to fetch data from my database whenever the users clicks a button, I know I can just make a flask route, using the flask restful module or plain routes. But my question is if I can make that route/endpoint not visible for users.
#app.route("/api/fetch/")
def fetch_data():
return some_data
I dont wan't the user having access to this endpoint directly, I just want the web application to be able to use it. Not sure if it is possible or where to look.
I found that maybe using Flask-CORS could help. Any help or guidance would be appreciated.
On flask you can use Blueprint
example from the above snippet would be
from flask import Blueprint
sample_bp = Blueprint("sample_bp", __name__)
#sample_bp.before_request
def restrict_with_token():
# Do something here on checking header or token
#sample_bp.route("/api/fetch/")
def fetch_api():
# Your logic
Otherwise you can have some referrence here : https://flask.palletsprojects.com/en/1.1.x/tutorial/views/
I'd like to use Flask's application factory mechanism fpr my application. I have is that the databases I use within some blueprints are located differently, so I'm using binds for pointing to them. The tables itself are in production and already in use, so I need to reflect them in order to use them within my application.
Problem is that I can't get the reflect function working because of the application context. I always get the message, that I'm working outside the application context. I fully understand that and see, that db is really outside, but don't have any idea anymore on how to involve it.
I tried different variations on passing app via current_app to my models.py, but nothing was working.
config.py:
class Config(object):
#Secret key
SECRET_KEY = 'my_very_secret_key'
ITEMS_PER_PAGE = 25
SQLALCHEMY_BINDS = {
'mysql_bind': 'mysql+mysqlconnector://localhost:3306/tmpdb'
}
SQLALCHEMY_TRACK_MODIFICATIONS = False
main.py:
from webapp import create_app
app = create_app('config.Config')
if __name__ == '__main__':
app.run(debug=true)
webapp/init.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(config_object):
app=Flask(__name__)
app.config.from_object(config_object)
db.init_app(app)
from main import create_module as main_create_module
main_create_module(app)
return app
webapp/main/init.py:
def create_module(app):
from .controller import blueprint
app.register(blueprint)
webapp/main/controller.py:
from flask import Blueprint, render_template, current_app as app
from .models import db, MyTable # <-- Problem might be here ...
bluerint = Blueprint('main', __name__)
#blueprint.route('/'):
def index():
resp = db.session.query(MyTable)\
.db.func.count(MyTable.versions)\
.filter(MyTable.versions =! '')\
.group_by(MyTable.name).all()
if resp:
return render_template('index.html', respo=respo)
else:
return 'Nothing happend'
webapp/main/models.py:
from .. import db # <-- and here ...
db.reflect(bind='mysql_bind')
class MyTable(db.Model):
__bind_key__ = 'mysql_bind'
__table__ = db.metadata.tables['my_table']
Expected result would be to get the reflection working in different blueprints.
Got it working, full solution here:
https://github.com/researcher2/stackoverflow_56885380
I have used sqllite3 for the test, run create_db.py script to setup db. Run flask with debug.sh, since recent versions you can't seem to just app.run() inside __main__ anymore.
Explanation
As I understand it a blueprint is just a way to group together several views if you need to use them multiple times in a single app or across multiple apps. You can add different route prefix as you desire.
A db object is not associated with a blueprint, it is associated with an app, which provide the configuration information. Once inside the blueprint views you will have access to the db object with the relevant app context automatically available.
Regarding the db.reflect, you need to make the call inside create_app and pass it the app object(preferred) or import the app inside the model which is spaghetti.
Multiple DBs can be accessed using binding as you've shown.
So your blueprints will have access to all tables imported and flask-sqlalchemy knows which db connection to use based on the binding.
I'm normally a fan of explicitly defining tables so you have access to the ORM objects and fields in code completion. Do you have lots of tables/fields or maybe you are creating something to query table metadata for total automation on any schema? Like a schema viewer or something like that.
This might be useful for others coming to this post:
https://flask-sqlalchemy.palletsprojects.com/en/2.x/contexts/
Brilliant! Thank you very much. Got it also working. Your tip gave me a hint to find another way:
#blueprint.route('/')
def index():
# pushing app_context() to import MyTable
# now I can use db.reflect() also in models.py
with app.app_context():
from .models import MyTable
results = db.session.query(MyTable).all()
print(results)
for row in results:
print (row)
print(row.versions)
print(row.name)
if results:
return render_template('my_table.html', results=results)
else:
return 'Nothing happend'
Then the reflection can be done inside models.py. The link you posted is really helpful, don't know why I did not stumble over it myself ...
Anyway, I do now have a lot more possibilities than before!
Cheers, mate!
I have a Flask application running at https://app.mydomain.com.
The blueprints look like this:
app.register_blueprint(main)
app.register_blueprint(account, url_prefix='/account')
app.register_blueprint(users, url_prefix='/users')
app.register_blueprint(boxes, url_prefix='/boxes')
app.register_blueprint(api_1_0, url_prefix='/api/v1.0')
The URLs look like this:
https://app.mydomain.com
https://app.mydomain.com/account
https://app.mydomain.com/users
...
I want to move the api_1_0 route from https://app.mydomain.com/api/v1.0 to https://api.mydomain.com, how should I modify the routes and how should I set app.config['SERVER_NAME']?
example.com (without any subdomain) is another site entirely, otherwise I would get rid of the app subdomain.
So, I want app to be the default subdomain for all blueprints except api_1_0 which should be api.
Since you want your Flask application to handle multiple subdomains, you should set app.config['SERVER_NAME'] to the root domain. Then apply app as the default subdomain and overriding it in api blueprint registration.
The way to do this would be something like that I suppose:
app.config['SERVER_NAME'] = 'mydomain.com'
app.url_map.default_subdomain = "app"
app.register_blueprint(account, url_prefix='/account')
app.register_blueprint(users, url_prefix='/users')
app.register_blueprint(boxes, url_prefix='/boxes')
app.register_blueprint(api_1_0, subdomain='api')