Combining Flask Blueprints with Flask-JWT - python-3.x

I am developing a Flask 0.12 app with Blueprints and I am trying to use Flask-JWT (0.3.2) for authentication.
None of the tutorials that I can see for Flask-JWT use Blueprints.
There is a usually a block of code in each example, that does something like this:
app = Flask(__name__)
jwt = JWT(app, authenticate, identity)
In a blueprinted flask app the structure tends to be more like this:
__init__.py:
from swarm.controllers.main import main
from swarm.controllers.grid import grid
from swarm.controllers.address import address
def create_app(object_name):
app = Flask(__name__)
...
app.register_blueprint(main)
app.register_blueprint(grid)
app.register_blueprint(address)
controllers/main.py:
main = Blueprint('main', __name__)
controllers/grid.py:
grid = Blueprint('grid', __name__)
controllers/address.py:
address = Blueprint('address', __name__)
How would I reference Flask-JWT and use its decorators in the controllers?
It might prove easier to answer this question by showing how to add JWT decorators to a standard example such as blueprintexample in the flask source code o Michał Karzyński's REST API demo.

I've built a simple example that enables use of Flask-JWT decorators.
The file structure:
server
├── app
| ├── admin # blueprint
| | ├── __init__.py
| | ├── views.py
| ├── __init__.py
├── config.py
└── run.py
First, create a blueprint called admin and import its views
# file: server/app/admin/init.py
from flask import Blueprint
admin = Blueprint('admin', __name__)
from . import views
The admin blueprint has one view. Here, we import the admin blueprint and some flask_jwt stuff so we can properly access their decorators:
from flask_jwt import jwt_required, current_identity
from . import admin
# file: server/app/admin/views.py
#admin.route('/protected')
#jwt_required()
def protected():
return '%s' % current_identity
Now create the flask app, create the JWT instance, and register the admin blueprint to the flask application:
# file: server/app/__init__.py
from flask import Flask
from flask_jwt import JWT
from werkzeug.security import safe_str_cmp
from .admin import admin
# skipping over jwt authenticate() and identity() creation
# https://pythonhosted.org/Flask-JWT/
# ...
app = Flask(__name__)
app.config.from_object('config')
jwt = JWT(app, authenticate, identity)
app.register_blueprint(admin, url_prefix='/admin')
And finally, set up your script manager:
from flask_script import Manager, Server
from server.app import app
manager = Manager(app)
# Turn on debugger by default and reloader
manager.add_command("runserver", Server(
use_debugger = True,
use_reloader = True,
host = '0.0.0.0',
port = 5000)
)
if __name__ == "__main__":
manager.run()
Now, if we go to our /admin/protected url, we will see that JWT is up and running:
{
"description": "Request does not contain an access token",
"error": "Authorization Required",
"status_code": 401
}
I hope this helps!

Related

Flask-SQLAlchemy not creating tables due to import order?

I know there are a few threads out there with the same issue, but all of them have different ways they are initialising their flask applications, and the answers are sort of tailored to their setup. Some have put their SQLAlchemy init in their models files, some have just one massive application.py file with declarations at the top, some put it in __ init__.py.
But I've structured my flask applications the following way since I am comforable with this pattern:
init.py
| \ \ \ \
| \ \ \ \
app.py models.py init_db.py some_db_helper.py module_with_db_needs.py
Basically I have an init.py file that contains all the initialisation code for a Flask app:
init.py
from flask import Flask
from flask_wtf.csrf import CSRFProtect
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SECRET_KEY"] = "abcd"
db = SQLAlchemy()
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///testdata.sqlite3"
db.init_app(app)
csrf = CSRFProtect()
csrf.init_app(app)
From there, any other file that I need can just import the needed objects. So models.py can import db from init.py for defining models, app.py can import app for flask routes, and some_db_helper.py can also import db, etc:
models.py
from sqlalchemy import Table, ForeignKey, ...
from init import db, app # Import app, db from init file
class Category(db.Model):
id = Column(Integer, primary_key=True, autoincrement=True)
# ... other properties
app.py
from init import app # Import app from init file
#app.route("/index.html", methods=["GET"])
def index():
return True
This file should create a new database file (I'm using sqlite), create the tables and data:
init_db.py
from models import * # Get app, db from models file
def insert_data():
# Insert some data into Category table
with app.app_context():
db.create_all()
insert_data()
However, when I run python init_db.py, my database file does get created, but it has no tables created at all. When the program tries to insert data, I get this error:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: category
I am aware that the db models need to be defined first, so I have my from models import * line, then doing create_all() at the end after all is loaded. You can also see init_db.py is pulling db from the import *, so it's using the same db instance as the models.py file, which means it should be aware of the defined models before creating the tables.
How can I get my init_db.py to create the tables properly using my current folder structure / import strategy?
I have read your comments, you have built some anti patterns, so rather than shoehorning this into working, let's go back to a healthier hierarchy.
. root of your app
+-- app.py (initialise app and routes)
+-- models.py (create models)
+-- init_db.py (db initial data, if you really don't want it in models.py)
app.py
All app initialisation, can be run with flask --app path/to/app.py run.
from flask import Flask
from init_db import insert_initial_data
from models import db
app = Flask(__name__)
app.config["SECRET_KEY"] = "abcd"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///testdata.sqlite3"
db.init_app(app)
with app.app_context():
db.create_all()
insert_initial_data()
#app.route("/index.html", methods=["GET"])
def index():
return b"OK"
models.py
All DB schema.
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Column, Integer, Table
db = SQLAlchemy()
class Category(db.Model):
id = Column(Integer, primary_key=True, autoincrement=True)
...
init_db.py
I'd argue that belongs in models.py if you need them in the database on init.
def insert_initial_data():
...
PS1. If you want to split out your routes I really recommend a blueprint, it as easy to use as the Flask instance in your app.
PS2. This is functional, it does run and creates the DB file.

Flask-login users are being logged out at random when app is live (not when run local)

I have a Flask app, with user Authentication. Its working fine when run in a venv but as soon as i deploy it as a google cloud app it starts logging users out at random, sometimes it can be minutes and other times it at one of the first requests.
Here are the most central parts of my app, I beleive the error must be here or in the App Engine configuration.
db=SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(12)
app.config['SQLALCHEMY_DATABASE_URI'] = "my_db_uri"
db.init_app(app)
from .views import views
from .auth import auth
app.register_blueprint(views, url_prefix='/')
app.register_blueprint(auth, url_prefix='/')
from .models import User
login_manager = LoginManager(app)
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
#login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
return app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
I was using os.urandom() to generate a random secret key in a settings file.
The problem was solved when I changed it to a string.
I guess the problem was that App Engine is running several instances and got differend secret keys from time to time, which made the session cookie invalid and therefor cleared the cookie content.
this link should show you how to set up environment variables on a production environment. https://dev.to/sasicodes/flask-and-env-22am
I think you are missing the os.getenv() which can be found by installing the dotenv module using pip install python-dotenv and importing it in your file either the config.py file or the file with the app engine configuration.
you can use the os.getenv as such
`
from dotenv import load_dotenv
load_dotenv()
db=SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv("my_secret_key")
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv("my_db_uri")
db.init_app(app)
from .views import views
from .auth import auth
app.register_blueprint(views, url_prefix='/')
app.register_blueprint(auth, url_prefix='/')
from .models import User
login_manager = LoginManager(app)
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
#login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
return app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
`

How to import app instance created from create_app()

How do I import app instance created in app/app.py's create_app() function to be used in app/api.py since the app instance returns only to my manage.py?
My project structure:
project/
app/
api.py
app.py
models.py
schema.py
...
manage.py
requirements.txt
README.md
I'm starting my dev app with command python manage.py
# manage.py
from app.app import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True, host='localhost', port=5000)
# app/app.py
def create_app():
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = config['mysql']['db_uri']
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {'pool_recycle': 3000, 'pool_pre_ping': True, }
register_extensions(app)
#app.before_first_request
def setup_logging():
gunicorn_error_logger = logging.getLogger('gunicorn.error')
app.logger.handlers.extend(gunicorn_error_logger.handlers)
app.logger.setLevel(gunicorn_error_logger.level)
return app
def register_extensions(app):
db.init_app(app)
api.init_app(app)
# app/api.py
from flask import Flask, jsonify
from flask_restful import Api, Resource
api = Api()
class TestGet(Resource):
def get(self):
app.logger.info('ok') ## Here I wish to use the app instance for logging
return 'ok'
api.add_resource(TestGet, '/ok')
How do I import the app
instance into app/api.py so that I may use app.logger.info(...)?
If I call create_app() it will create a new instance and not the one that is already running?
There is no need to import app.
You can use the current_app helper.
from flask import current_app
You can think of it as a proxy to the app instance.
Links to the official documentation:
https://flask.palletsprojects.com/en/1.1.x/appcontext/#the-application-context
https://flask.palletsprojects.com/en/1.1.x/api/#flask.current_app
Miguel Grinberg also offers very good information on this topic:
https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xv-a-better-application-structure
(search for current_app on the page)

Pytest Flask Application AttributeError: module 'src.api' has no attribute 'test_client'

I am trying a basic Pytest test for a "hello world" flask application
Please see below what I have in the src file
api.py:
from flask import Flask, jsonify
api = Flask(__name__)
#api.route('/')
def hello_world():
return jsonify(message="hello world")
if __name__ == '__main__':
api.run(debug=True)
and this is what I have written for the test
test_api.py
import pytest
from src import api
api.testing = True
client = api.test_client()
def test_route(client):
response = api.get('/')
assert response.status_code == 200
Structure
my_project
__init__.py
src
__init__.py
api.py
test
__init__.py
test_api.py
I run the tests from the root with python -m pytest
The error message I get is
test/test_api.py:13: in <module>
with api.test_client() as client:
E AttributeError: module 'src.api' has no attribute 'test_client'
I am really unsure of how to make this work.
from src import api imports the module of src/api.py
However, you are interested in the global api object that is within the scope of that src.api module
from src.api import api will import the flask application object which should have the test_client method that you are calling.

Why do I have to return app in Flask create_app?

from flask import Flask, render_template
from flask.ext.bootstrap import Bootstrap
from flask.ext.mail import Mail
from flask.ext.moment import Moment
from flask.ext.sqlalchemy import SQLAlchemy
from config import config
bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app)
#
return app
Hi, I'm learning Flask by reading 'Flask Web Development by Grinberg M'.
The above code is copied from that book.
Could anyone please explain why I need to "return app" in the end?
As far as I know you haven't copied the complete code.
You've probably missed these lines or something similar:
if __name__ == '__main__':
app = create_app('Example')
app.run()
The reason the author returns the app, as #davidism has explained, is that we use the app instance to start our Flask application. Simple as that!
I hope this answers your question.

Resources