Removing global app reference from blueprint in Flask - python

I'm currently working on learning Flask and create a working page with login functions. Now I want to remove the global app instance and started using blueprints for the submodules.
My project is structured like this:
+ app
+ auth
- __init__.py
- forms.py
- routes.py
+ main
+ models
+ templates
- __init__.py
+ migrations
- index.py
- config.py
No I added a blueprint to the routes.py and used the decorators there:
from flask import render_template, flash, redirect, url_for, request, Blueprint
from app import app, db
from app.auth.forms import LoginForm, RegistrationForm
# ...
from app.models.User import User
blueprint = Blueprint('auth', __name__)
#blueprint.route('/login', methods=['GET', 'POST'])
def login():
return "example login"
The __init__.py of the auth module:
from . import forms, routes
The blueprint gets added in the __init__.py of the app folder:
# ...
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
from app.auth.routes import blueprint as auth_bp
app.register_blueprint(auth_bp)
After using the #blueprint decorators, I don't need #app anymore, but how can I access the db when I want to remove the import app and the from app.models.User import User part?

from . import db
from ..models.User import User

There are two things to understand here. app as a module (the folder) and app the instance of flask inside __init__.py. When you do import app inside authentication blueprint then you are actually importing the whole app module and not the flask instance. When you do from app import app you are actually importing flask instance from app module. This can be confusing to eliminate this I advise you to change the name of app folder to something different like bacher then when you need to import db inside your authentication blueprint use from bacher import db and for User model from bacher.models.User import User

Related

My flask application factory returns URL not found

My flask app is based on Miguel Grinberg's mega tutorial XIV with some extra stuff and it was all working fine and could be accessed from browser on localhost:5000. I decided to switch to an application factory approach as per Grinberg's XV tutorial BUT with no blueprints. Now when I enter localhost:5000 I get a URL not found. I am guessing my routes.py is not being picked up for some reason.
I have thoroughly worked through Grinberg's XV tutorial and the associated code and aligned everything less blueprints. These are the links I have explored that I have based my current app on: -
https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xv-a-better-application-structure
http://flask.pocoo.org/docs/1.0/tutorial/factory/
Dave W Smith's answer in Configure Python Flask App to use "create_app" factory and use database in model class
and others.
From what I have read implementing an application factory should be really simple. The examples work.
The flask server starts from the command prompt in a venv as always.
Here is my directory structure.
myapp
app.py
config.py
...
/app
__init__.py
routes.py
models.py
forms.py
...
Here is the code slightly simplified for clarity.
# app.py
#=============================================
from app import create_app, db
from app.models import User, Post
app = create_app()
#app.shell_context_processor
def make_shell_context():
return {'db': db, 'User': User, 'Post' :Post}
# __init__.py
#=============================================
...
import os
from flask import Flask, request, current_app
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
...
from config import Config
db = SQLAlchemy()
migrate = Migrate()
...
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
migrate.init_app(app, db)
...
# logging, email servers, etc configured
return app
from app import models
# routes.py
#=============================================
from datetime import datetime
from flask import render_template, flash, redirect, url_for, request, g, \
jsonify, current_app
...
from app import app, db
from app.forms import LoginForm, RegistrationForm, EditProfileForm,
PostForm,ResetPassword,RequestForm, ResetPasswordForm
from app.models import User, Post
...
#app.route('/', methods=['GET', 'POST'])
#app.route('/index', methods=['GET', 'POST'])
#login_required
def index():
form = PostForm()
if form.validate_on_submit():
...
page = request.args.get('page', 1, type=int)
...
return render_template('index.html', title=_('Home'), form=form,
posts=posts.items, next_url=next_url,
prev_url=prev_url)
#app.route ....
# models.py
#=============================================
from datetime import datetime
from hashlib import md5
from time import time
from flask import current_app
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
import jwt
from app import db, login
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
...
This may be irrelevant but it is one thing I tried. Note in routes.py that I have from app import app, db, if I remove app and add from flask import current_app then the #app decorators show as undefined.
Another point, because its a problem with routes. If I change the last line of __init__.py to from app import routes, models I get a different error.
flask.cli.NoAppException
flask.cli.NoAppException: While importing "metapplica", an ImportError was raised:
Traceback (most recent call last):
File "c:\users\markko~1\dropbox\python\projects\metapp~1\venv\lib\site-packages\flask\cli.py", line 236, in locate_app
__import__(module_name)
File "C:\Users\Mark Kortink\Dropbox\Python\projects\metapplica\metapplica.py", line 1, in <module>
from app import create_app, db, cli
File "C:\Users\Mark Kortink\Dropbox\Python\projects\metapplica\app\__init__.py", line 75, in <module>
from app import routes, models
File "C:\Users\Mark Kortink\Dropbox\Python\projects\metapplica\app\routes.py", line 8, in <module>
from app import app, db
ImportError: cannot import name 'app' from 'app' (C:\Users\Mark Kortink\Dropbox\Python\projects\metapplica\app\__init__.py)
It was this error that led me to try the current_app change mentioned above.
I start the app like this.
cd C:\Users\...\myapp
venv\Scripts\activate
set FLASK_APP=app.py
set FLASK_DEBUG=1
flask run
I know the problem is probably really basic but can anyone see why the above code would give a URL not found or not be able to find the routes?
=== NEXT PHASE ================
Following the recommendations from #silver below my new names are: -
myapp
runapp.py
...
/mapp
__init__.py
...
I went through all my code and changed from app[.xxx] import yyy to from myapp[.xxx] import yyy. This flushed out a few new errors where I was referencing app which I fixed by substituting current_app.
My new error is
RuntimeError
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.
Traceback (most recent call last)
File "C:\Users\Mark Kortink\Dropbox\Python\projects\myapp\runapp.py", line 1, in <module>
from mapp import create_app, db, cli
File "C:\Users\Mark Kortink\Dropbox\Python\projects\myapp\mapp\__init__.py", line 75, in <module>
from mapp import routes, models
File "C:\Users\Mark Kortink\Dropbox\Python\projects\myapp\mapp\routes.py", line 16, in <module>
#current_app.before_request
Without being able to run it myself it's a little hard to be sure.
But I think this is the crux of the matter:
This may be irrelevant but it is one thing I tried. Note in routes.py that I have from app import app, db, if I remove app and add from flask import current_app then the #app decorators show as undefined.
If you want to register anything to the app that's actually running, it has to be through from flask import current_app. That's the thing about Flask application factories -- you only get access to the name of the application that's actually running in two places:
In the factory function itself: create_app, before you return the app object, and
Via from flask import current_app
It looks like Python is able to successfully import the name app in routes.py, since you say the application starts. The only place that from app import app could resolve to is from your app.py file at the top level of your myapp package. That means that each time routes.py is initialized, it's calling the create_app function, and getting a new app object. So understandably, the top-level app object that Flask is serving is not the same one that has routes registered to it.
I recommend renaming your files so that nothing has the name "app" except the object returned by create_app.
Then, in routes.py try:
from datetime import datetime
from flask import render_template, flash, redirect, url_for, request, g, \
jsonify
from flask import current_app as app_ref
...
from app_internals import db # assuming you've renamed the app package
from app_internals.forms import LoginForm, RegistrationForm, EditProfileForm,
PostForm,ResetPassword,RequestForm, ResetPasswordForm
from app_internals.models import User, Post
...
current_app = app_ref._get_current_object()
#current_app.route('/', methods=['GET', 'POST'])
#current_app.route('/index', methods=['GET', 'POST'])
#login_required
def index():
form = PostForm()
if form.validate_on_submit():
...
page = request.args.get('page', 1, type=int)
...
return render_template('index.html', title=_('Home'), form=form,
posts=posts.items, next_url=next_url,
prev_url=prev_url)
#current_app.route ....
In case someone is struggling with similar issues (flask 'mysteriously' not serving routes that should be there, perhaps in conjunction with a create_app factory method):
flask routes is actually showing the routes it detects. the output looks like this (for just one 'hello' endpoint at the root url):
Endpoint Methods Rule
-------- ------- -----------------------
hello GET /
static GET /static/<path:filename>
the SERVER_NAME setting can be responsible (this was the case for me). I had changed it to 0.0.0.0 without realizing it makes a difference for the routing. There is a bug report/discussion on github on that topic.
I just ran into this issue too with Miguel Grinberg's tutorial. It's a pretty fantastic tutorial till some of the later sections which feel rushed.
Anyway.
You need to pass the context.
In the factory function you need to do.
def create_app(config_class=Config):
app = Flask(__name__)
with app.app_context():
from app import routes
Then like the person posted above.
for routes.py change all the #app to #current_app
from flask import current_app
#current_app.route('/')
#current_app.route('/index', methods=['GET', 'POST'])
#login_required
def index():
this works for me.

How to add views to a blueprint in separate files

I have a huge application that was getting hard to update its views. To 'fix' this, I had separate the views into a few files, using blueprints. The problem is that the blueprints are also getting very big, because the long documentation that each view has and the different verifications that each view requires.
I had tried to do an import like this:
Where I have a main file that contains the Flask application (which imports the blueprint), a file that contains the blueprint and a file the imports the blueprint and configure the views in it. The problem is that with this approach the views are not rendered, because flow reasons.
The main file, in the root of a folder:
from flask import Flask
from source import test
application = Flask(__name__)
application.register_blueprint(test)
application.run()
The blueprint file, inside a subfolder in the root folder:
from flask import Blueprint
test = Blueprint('test', __name__)
The view file, inside the same subfolder as the blueprint file:
from .test import test
#test.route('/home', methods=['GET', 'POST'])
def home():
return 'home'
I had also tried to add the blueprint decorator to a declared function, this way the views are add to the blueprint in the blueprint file, but I don't think this is a good approach or a scalable approach - and it didn't work ^ - ^.
I expect to create a blueprint in a file, import the blueprint in other files and add views to the blueprint and then import the blueprint and add it to the Flask application.
You need to import the views content in blueprint file.
I have created the scenario and able to get the view. Additionally, I have updated the naming convention.
Folder structure:
.
├── app.py
└── blueprints
├── example_blueprint.py
├── example_views.py
└── __init__.py
app.py:
from flask import Flask
from blueprints.example_blueprint import bp
app = Flask(__name__)
app.register_blueprint(bp)
blueprints/example_blueprint.py:
from flask import Blueprint
bp = Blueprint('bp', __name__,
template_folder='templates')
from .example_views import *
blueprints/example_views.py:
from .example_blueprint import bp
#bp.route('/home', methods=['GET', 'POST'])
def home():
return 'home'
blueprints/__init__.py: Blank file
Output:
Running the application:
export FLASK_APP=app.py
export FLASK_ENV=development
flask run
requirements.txt:
Click==7.0
Flask==1.0.3
itsdangerous==1.1.0
Jinja2==2.10.1
MarkupSafe==1.1.1
pkg-resources==0.0.0
Werkzeug==0.15.4
Reference:
Flask documentation on Blueprints
In root folder change main file:
from flask import Flask
from source.listener import test
application = Flask(__name__)
application.register_blueprint(test)
application.run()
The blueprint file, inside a subfolder in the root folder:
listener.py
from flask import Blueprint
from source.view import home
test = Blueprint('test', __name__)
test.add_url_rule('/home', view_func=home,methods=['GET', 'POST'])
The view file, inside the same subfolder as the blueprint file:
from flask import request
def home():
if request.method == 'POST':
user = request.form['name']
return "Welcome "+user
else:
return 'home'
Get Request O/P:
Home
Post Request O/P:
Welcome username
The view module isn't discovered since only the Blueprint object is imported.
From the organization of your Blueprint and particularly the import that you have shared in your main file, I can deduct the existence of an __init__.py in the blueprint folder that exports the blueprint object.
Importing the views in that file should have the app discover the views registered in the blueprint.
i.e.
blueprint/__init__.py:
from .test import test
from . import views

Flask blueprints cannot import module

I'm learning Blueprints for Flask, but I am having trouble with importing the correct modules. This is my setup:
Folder structure:
- app.py
templates/
nomad/
- __init__.py
- nomad.py
app.py
from flask import Flask
from nomad.nomad import nblueprint
app = Flask(__name__)
app.register_blueprint(nblueprint)
nomad.py
from flask import render_template, Blueprint, abort
from app import app
nblueprint = Blueprint('nblueprint', __name__, template_folder='templates')
# Routes for this blueprint
#app.route ....
__init__.py is empty
The error I'm getting: ImportError: cannot import name nblueprint. I know my import statement is probably wrong, but what should it be and why?
EDIT:
If I remove from app import app, then I can successfully import nblueprint in app.py. But I need app in nomad.py because it needs to handle routes. Why is that line causing issues with importing, and how would I get around this?
Blueprints is for define application route so you don't need to use app instance and blueprint in same place for route defination.
#nomad.py
#nblueprint.route('/')
You are getting error because while you register the blueprint at the same time you use app instance. So as you said when you remove the from app ... it solve the problem.
The recommend way is define your view for that blueprint in blueprint package in your example nomad package, it should be like this:
...
nomad/
__init__.py
views.py
#nomad/__init__.py
nblueprint = Blueprint(...)
#nomad/views.py
from . import nblueprint
#nblueprint.route('/')
...

how to use config in flask extensions when use factory pattern in flask?

I use factory pattern and flask extensions such as flask-admin during the development of web application. I want to load some configurations in flask-admin when the app haven't created. So i use the current_app.config["SOME_CONFIG"] to get the config value.But i got the Working outside of application context. exception. The code as follows:
# __init__.py
from flask import Flask
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config_name)
app.config.from_pyfile("config.py")
from admin import admin
admin.init_app(app)
return app
# admin/__init__.py
from flask import current_app
from flask_admin import Admin
admin = Admin(name=current_app.config["ADMIN_NAME"], template="bootstrap2")
Your application is still in the setup state during the create_app function (see http://flask.pocoo.org/docs/0.11/appcontext/). During the setup state you need to have a reference to the application object to access it, you can't use current_app.
You could instantiate the Admin object within the create_app function using:
admin = Admin(name=app.config["ADMIN_NAME"], template="bootstrap2")
OR
change the admin variable and create a function in your admin/__init__.py:
admin = object()
def instantiate_admin(config):
global admin
admin = Admin(name=config["ADMIN_NAME"], template="bootstrap2")
return admin
and use this in create_app():
from admin import instantiate_admin
admin = instantiate_admin(app.config)
admin.init_app(app)
All you need to do is set it up like the following.
# config.py
class Config(object):
ADMIN_NAME = 'admin'
# __init__.py
from flask import Flask
from flask_admin import Admin
# initiate all extensions here
admin = Admin(template='bootstrap2')
def create_app(config):
app = Flask(__name__)
app.config.from_object(config)
admin.init_app(app)
return app
# wsgi.py
from . import create_app
from config import Config
app = create_app(Config)
if __name__ == '__main__':
app.run()
And in you admin package
# admin/__init__.py
from .. import admin # import extensions from __init__.py
admin.add_view('blah')
Below is a link to an real world example. I scrubbed info so it is more generic but this is how i setup all my flask apps.
http://hastebin.com/likupoxoxi.py
As long as you are running the dev server you shouldn't have issues that keeps it in the context of the application.

How to structure Flask User app?

I use supervisor to run my app. It is structured as follows:
My app layout
my_app
__init__.py
my_app
__init__.py
startup
create_app.py
create_users.py
common_settings.py
core
__init__.py
models.py
views.py
Outer __init__.py
from my_app import app
Inner __init__.py
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__) # The WSGI compliant web application object
db = SQLAlchemy(app) # Setup Flask-SQLAlchemy
manager = Manager(app) # Setup Flask-Script
from my_app.startup.create_app import create_app
create_app()
create_app.py
def create_app(extra_config_settings={}):
"""
Initialize Flask applicaton
"""
# ***** Initialize app config settings *****
# Read common settings from 'app/startup/common_settings.py' file
app.config.from_object('app.startup.common_settings')
# Read environment-specific settings from file defined by OS environment variable 'ENV_SETTINGS_FILE'
app.config.from_envvar('ENV_SETTINGS_FILE')
# Load all blueprints with their manager commands, models and views
# Setup Flask-User to handle user account related forms
from my_app.core.models import User
# Setup Flask-User
db_adapter = SQLAlchemyAdapter(db, User) # Setup the SQLAlchemy DB Adapter
user_manager = UserManager(db_adapter, app) # Init Flask-User and bind to app
from my_app import core
return app
my_app/core/__init__.py
from . import models
from . import views
views.py
from my_app import db, app
'''
Register a new user
'''
#app.route('/register', methods = ['POST'])
def register_user():
user_manager = app.user_manager
db_adapter = user_manager.db_adapter
I was trying to follow an example I found online.
I'm creating the variables db_adapter and user_manager in create_app(). Are these the same ones being used in my views.py?
If anyone has any suggestions or links to examples that I can follow to structure my project, it would be greatly appreciated.
Thanks.
Assuming that that's how Flask-User works (sets the user_manager attribute on app), this is trivial to determine, just compare them in the create_app function when you still have a direct reference to the objects.
db_adapter = SQLAlchemyAdapter(db, User)
user_manager = UserManager(db_adapter, app)
assert db_adapter is user_manager.db_adapter
assert user_manager is app.user_manager
However, your entire project layout doesn't make much sense. You should be creating the entire app inside the create_app factory. You should not have an __init__.py file at the top level, that's the project folder not the package. You should use current_app within views to access the app, since it will only be created at runtime by the factory. You should create a manage.py file at the project level to use the factory.
my_project/
my_app/
__init__.py
models.py
views.py
defaults.py
instance/
config.py
manage.py
__init__.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__, instance_relative_config=True)
app.config.from_object('my_app.defaults')
app.config.from_pyfile('config.py')
db.init_app(app)
from my_app.views import bp
app.register_blueprint(bp)
return app
models.py:
from my_app import db
class User(db.Model):
...
views.py:
from flask import Blueprint, render_template
from my_app.models import User
bp = Blueprint('app', __name__)
#bp.route('/')
def index():
return render_template('index.html')
manage.py:
#!/usr/bin/env python
from flask_script import Manager
from my_app import create_app
Manager(create_app).run()

Categories