I have two application factory functions - one creates the "customer" app, and the other creates the "admin" backend app. Both of the factory functions essentially do what is described here - create a flask app and register some extensions to it and then add some blueprints(with a url_prefix). I glue the two apps together via the create_combined_app() from below. It is the return value of that function which I register with my Flask-Script's Manager.
def create_combined_app(config_name):
customer_app = create_customer_app(config_name)
admin_app = create_admin_app(config_name)
from werkzeug.wsgi import DispatcherMiddleware
customer_app.wsgi_app = DispatcherMiddleware(customer_app.wsgi_app, {
'/admin': admin_app
})
return customer_app
And then this is how I run it.
def make_me_an_app():
return create_combined_app(config)
manager = Manager(make_me_an_app)
...
if __name__ == '__main__':
manager.run()
I want to do some testing which involves getting all GET routes of my app and making sure they load. I followed the example from here, but I only see the urls of the customer app, and none of the urls from the admin backend.
#main.route("/site-map")
def site_map():
from flask import current_app, jsonify
links = []
app = current_app
for rule in app.url_map.iter_rules():
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint))
return jsonify(links)
The admin backend works when I try to access it from the browser - it all works nicely, except that I don't see the admin's urls when I call /site-map.
Thanks! :)
I think DispatcherMiddleware create separate apps. Which mean you created customer_app and admin_app. Those 2 live as standalone. They don't know each others, therefor current_app is just the show customer_app.
Here is the describe from Flask http://flask.pocoo.org/docs/0.12/patterns/appdispatch/
Related
The flask-rest-jsonapi quickstart shows that you can create a route() like this:
api.route(PostList, 'post_list', '/posts')
api.route(PostDetail, 'post_detail', '/posts/<int:id>')
But I want to have all of my routes to be something like /api/posts and /api/poss/<int:id> and I want to avoid repeating the /api part in every route(). When I try to use a blueprint here, like this:
api_bp = Blueprint('API', __name__, url_prefix='/api')
api = Api(app, api_bp)
api.route(PostList, 'post_list', '/posts')
api.route(PostDetail, 'post_detail', '/posts/<int:id>')
app.register_blueprint(api_bp)
The endpoint is still /posts and not /api/posts. How do I properly make a URL prefix for all the routes?
Reading the discussions on Github do the following:
# Create blueprint
api_bp = Blueprint('API', __name__, url_prefix='/api')
# Create Api instance only passing the blueprint
api = Api(blueprint=api_bp)
# register routes
api.route(PostList, 'post_list', '/posts')
api.route(PostDetail, 'post_detail', '/posts/<int:id>')
# initialize Api instance to App
api.init_app(app)
Don't forget that the view names will change. i.e view 'post_list' becomes 'API.post_list' so you have to adjust your schemes, but not your route declarations. This is also discussed in the linked Github discussion.
You can use flask and Flask-RESTful here. You just need to create flask APP first and configure Flask-RESTful API with prefix.
Here is the example
from flask import Flask, request
from flask_restful import Api
app = Flask(__name__)
api = Api(app, prefix="/api")
Now you can add resource to the api.route as below
import types
api.route = types.MethodType(api_route, api)
def api_route(self, *args, **kwargs):
"""Add resource to the api route"""
def wrapper(cls):
self.add_resource(cls, *args, **kwargs)
return cls
Tech Stack
Python API
Flask Framework
We have developed Python API which then will be consumed by our React UI.
We have URL as follows
http://client-be.domain.com/admin
We want to change url to some random string
http://client-be.domain.com/fvcd9e72
But i am not able to find out where exactly i need to change this in my code.
I can see "admin" folder inside my API code and inside view, code is as follows
admin_views = view1 + view2 + view3 & so on...
So does it means, if i change folder name to random string, my url will be changed?
you can achieve this by adding an argument in your api end point. rest you can handle the situation/conditions in that itself
eg
from flask import Flask
app = Flask(__name__)
#app.route('/<stre>')
def home(stre):
return stre
if __name__ == '__main__':
app.run()
You can set custom url instead of 'admin'. Admin is default url in Flask.
eg:
from flask_admin import Admin
admin = Admin(name='app_name', url='/your_string')
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?
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.
I'm currently using the Flask Application Factory pattern with Blueprints. The issue that I'm having is how do I access the app.config object outside of the application factory?
I don't need all the configuration options from the Flask app. I just need 6 keys. So the current way I do this is when the create_app(application factory) is called, I basically create a global_config dictionary object and I just set the global_config dictionary to have the 6 keys that I need.
Then, the other modules that need those configuration options, they just import global_config dictionary.
I'm thinking, there has to be a better way to do this right?
So, on to the code
My current init.py file:
def set_global_config(app_config):
global_config['CUPS_SAFETY'] = app_config['CUPS_SAFETY']
global_config['CUPS_SERVERS'] = app_config['CUPS_SERVERS']
global_config['API_SAFE_MODE'] = app_config['API_SAFE_MODE']
global_config['XSS_SAFETY'] = app_config['XSS_SAFETY']
global_config['ALLOWED_HOSTS'] = app_config['ALLOWED_HOSTS']
global_config['SQLALCHEMY_DATABASE_URI'] = app_config['SQLALCHEMY_DATABASE_URI']
def create_app(config_file):
app = Flask(__name__, instance_relative_config=True)
try:
app.config.from_pyfile(config_file)
except IOError:
app.config.from_pyfile('default.py')
cel.conf.update(app.config)
set_global_config(app.config)
else:
cel.conf.update(app.config)
set_global_config(app.config)
CORS(app, resources=r'/*')
Compress(app)
# Initialize app with SQLAlchemy
db.init_app(app)
with app.app_context():
db.Model.metadata.reflect(db.engine)
db.create_all()
from authenication.auth import auth
from club.view import club
from tms.view import tms
from reports.view import reports
from conveyor.view import conveyor
# Register blueprints
app.register_blueprint(auth)
app.register_blueprint(club)
app.register_blueprint(tms)
app.register_blueprint(reports)
app.register_blueprint(conveyor)
return app
An example of a module that needs access to those global_config options:
from package import global_config as config
club = Blueprint('club', __name__)
#club.route('/get_printers', methods=['GET', 'POST'])
def getListOfPrinters():
dict = {}
for eachPrinter in config['CUPS_SERVERS']:
dict[eachPrinter] = {
'code': eachPrinter,
'name': eachPrinter
}
outDict = {'printers': dict, 'success': True}
return jsonify(outDict)
There has to be a better way then passing a global dictionary around the application correct?
There is no need to use global names here, that defeats the purpose of using an app factory in the first place.
Within views, such as in your example, current_app is bound to the app handling the current app/request context.
from flask import current_app
#bp.route('/')
def example():
servers = current_app.config['CUPS_SERVERS']
...
If you need access to the app while setting up a blueprint, the record decorator marks functions that are called with the state the blueprint is being registered with.
#bp.record
def setup(state):
servers = state.app.config['CUPS_SERVERS']
...