Can I keep all Flask blueprints in one file? - python

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))
# ...

Related

Flask-SQLAlchemy application context error

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?

Python: using database instance across submodules

I'm writing a Flask app and I instantiate a mongo database in main.py.
I've got a submodule called user.py that holds class User. main.py takes login credentials and sends it to the class User which handles the rest.
How can I cleanly pass my mongo instance to the User class? I've tried static variables in a config.py file but they don't work because the variables are always None when user.py tries to use them.
Right now I'm resorting to passing in mongo as a parameter, but this seems like a dirty way to do it considering there will be many modules. Here's my code;
# Setup app and database
app = Flask(__name__)
app.config['MONGO_URI'] = 'mongodb://'
mongo = PyMongo(app)
You can import mongo directly into your user.py module.
To avoid circular import error, you just have to move the imports to the bottom of the file. As long as you import User into main after app is defined, it should resolve the circular import:
user.py
from .main import mongo
# class User():
# ... Your Code
main.py
from flask import Flask
from flask_pymongo import PyMongo
app = Flask(__name__)
mongo = PyMongo(app)
from .user import User
Moving imports to the bottom are normally not a good idea, but in Flask is quite common. Below is an example of a similar scenario from the official flask documentation:
# app.py
from flask import Flask
app = Flask(__name__)
import app.views
# views.py
from app import app
#app.route('/')
def index():
return 'Hello World!'
http://flask-.readthedocs.io/en/0.6/patterns/packages/#simple-packages
Do the importing at the bottom of the file [...]
Every Python programmer hates them, and yet we just added some: circular imports (That’s when two modules depend on each other. In this case views.py depends on init.py). Be advised that this is a bad idea in general but here it is actually fine. The reason for this is that we are not actually using the views in init.py and just ensuring the module is imported and we are doing that at the bottom of the file.

Access Flask config outside of application factory

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']
...

Structuring Flask App with a Helper Class

In order to simplify the __init__.py main module, I want to push helper functionality to a different file/class. This requires passing many flask extensions instances when initializing the class, which seems inelegant. My current structure is as follows:
__init__.py:
from flask import Flask, render_template,request
from flask.ext.sqlalchemy import SQLAlchemy
from flask_mail import Mail
from FEUtils import FEUtils
# .. and more imports of various extensions ..
db = SQLAlchemy()
app = Flask(__name__)
db.init_app(app)
mail = Mail(app)
fe_utils = FEUtils(db,mail,app.config)
# Flask code..
if __name__ == '__main__':
app.run()
and FEUtils.py:
from models import User
class FEUtils(object):
def __init__(self,db,mail,config):
self.session = db.session # to access database
self.mail = mail # to send emails
self.config = config # to access app config dictionary
def count_users(self): # example helper method
return self.session.query(User).count()
This all works fine, but seems cumbersome. I'd like the helper class to inherit the various extension instances from the main module, and be able to access the flask config parameters from within the helper class, without passing each when the helper class is instantiated.
Asked differently, is there a way to have the helper class behave as if each of its methods was defined in the main module in an elegant way?

Using Flask-pymongo across multiple modules

I'm having some trouble understanding how to incorporate Flask-Pymongo. My app is initiated from my rrapp.py Inside of this file, I have
rrapp.py
#
# Imports up here
#
app = Flask(__name__)
mongo = PyMongo(app)
# Code down here
Now, to use this, I simply do mongo.db.users.find(). This works fine.
Now, say I have another file called userservice.py that I call methods from one of my endpoints within rrapp.py. How do I incorporate PyMongo(app) in my userservice.py file if I don't have access to the app object? Or am I missing something obvious here?
you should first define mongo oustside create_app to have access to it from inside other files.
then init_app with that like the following:
from flask import Flask, current_app
from flask_pymongo import PyMongo
mongo = PyMongo()
def create_app(config_name):
app = Flask(__name__, instance_relative_config=False)
app.config.from_object(app_config[config_name])
# INIT EXTENSIONS ----------------------
mongo.init_app(app)
return app
then in any file you can import mongo from above file. for example:
from ../factory import mongo

Categories

Resources