How to add multiple navbars in flask? - python

I have a primary navigation and now I want to add a secondary navigation within a view. Kind of like stackoverflow has [interesting, bounty, hot, week, month].
I have tried this but get internal server errors. How do I implement this in my blueprint properly?
My blueprint:
from flask import Blueprint, render_template, redirect, url_for, flash
from flask import current_app as app
from flask_login import login_required
myView_bp = Blueprint(
'myView_bp', __name__,
template_folder='templates',
static_folder='static'
)
#myView_bp.route('/myView', methods=['GET', 'POST'])
#login_required
def myView():
nav.context.Item('interesting', 'interesting_bp.interestingStuff')
nav.context.Item('bounty', 'bounty_bp.bountyStuff')
nav.context.Item('hot', 'hot_bp.hotStuff')
return render_template('myView.html')
My application init:
...
from flask_navigation import Navigation
...
def create_app(config_class=Config):
...
nav = Navigation(app)
nav.init_app(app)
nav.Bar('top', [
nav.Item('Home', 'home_bp.home'),
nav.Item('profile', 'profile_bp.testPage'),
nav.Item('new', 'new_bp.analyzePage'),
])
nav.Bar('context')
with app.app_context():
...
return app

Related

Flask Session loses login session data on refresh

I'm currently building a Chat App using Flask and Flask Socketio. In order to save the client's current chat-room, I use the flask_session module with the session type "filesystem". I also use Flask-Login module so the client is logged in with an account. So when I log the client in, the session contains the client's login data from the Flask-Login module. But as soon as I refresh the chat page, the Flask_login data is gone and the current user variable from the Flask-Login module becomes an AnonymousMixin object which causes my program to crash. So what causes the FileSystemSession to lose the current_user data after refreshing the page?
This is the output when I print the session and the current_user before and after the refresh of the page:
Before:
<FileSystemSession {'_permanent': True, 'csrf_token': 'f687c55dbf48c0b9cd1e4601729ba687f74e255b', 'current_room': 'Lobby', '_fresh': True, '_user_id': '1', '_id': '4825ca5a26d88cfc91bcb75ef854505f0d4cdc736affaac2fd3211fb6015b34490c6ec47ec7175dee531442ec8d3a1d5f092c4de1349ca0d48fe723aed678c5a', '_flashes': [('success', 'Logged in successfully')]}>
<User 1>
After:
<FileSystemSession {'_permanent': True, 'csrf_token': 'f687c55dbf48c0b9cd1e4601729ba687f74e255b', 'current_room': 'Lobby'}>
<flask_login.mixins.AnonymousUserMixin object at 0x000002CD3CFB6910>
Here's my code:
__init__.py
from flask import Flask
from flask_session import Session
from flask_login import LoginManager
from .models import *
from os import path
app = Flask(__name__)
def create_app():
app.config["SECRET_KEY"] = "secret!"
app.config["SESSION_TYPE"] = "filesystem"
app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{DB_NAME}"
login_manager = LoginManager()
login_manager.init_app(app)
Session(app)
#login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
db.init_app(app)
from .application import socketio
from .views import views
from .auth import auth
app.register_blueprint(views, url_prefix="/")
app.register_blueprint(auth, url_prefix="/")
return socketio, app
views.py
from flask import Blueprint, render_template, flash, redirect, url_for, request, session
from flask_login import current_user, login_required
views = Blueprint("views", __name__)
#views.route("/", methods=["GET", "POST"])
def index():
return render_template("index.html", user=current_user)
#views.route("/chat", methods=["GET", "POST"])
def chat():
return render_template("chat.html", user=current_user, rooms=current_user.rooms, current_room=Room.query.filter_by(room_name=session["current_room"]).first())
I don't know if this is important but I also put in the flask_socketio part, which changes the session when the room is changed:
from flask import session
from flask_socketio import SocketIO, emit, send, join_room, leave_room
from app import app
socketio = SocketIO(app, manage_session=False)
#socketio.on('join')
def on_join(data):
user = data["username"]
room = data["room"]
join_room(room)
session["current_room"] = room
emit("room-manager", {"message": f"{user} has joined the {room} room"}, room=room)

how to implement Flask-Dance with Flask Blueprints

I tried to use Flask-Dance with normal flask app and it works and if I try to implement with flask blueprints it doesn't work. How to register flask-dance to flask blueprints?
My views.py for auth blueprint
from flask import render_template, url_for, redirect, current_app, request
from app.auth import auth
from flask_dance.contrib import github
#auth.route('/login')
def login():
return render_template('auth/login.html')
#auth.route("/")
def github():
if not github.authorized:
return redirect(url_for("github.login"))
resp = github.get("/user")
assert resp.ok
return "You are #{login} on GitHub".format(login=resp.json()["login"])
my init.py for auth blueprint
from flask import Blueprint
from flask_dance.contrib.github import make_github_blueprint, github
auth = Blueprint('auth', __name__, url_prefix='/auth')
blueprint = make_github_blueprint(client_id="m-client-id",client_secret="my-client-secret")
auth.register_blueprint(blueprint, url_prefix="/auth")
from app.auth import views
and my main init.py file:
from flask import Flask
from flask_fontawesome import FontAwesome
from app.config import Config
fa = FontAwesome()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
fa.init_app(app)
from app.public import public
app.register_blueprint(public)
from app.auth import auth
app.register_blueprint(auth)
return app
First you should create and register different blueprint for github.
github/init.py
from flask_dance.contrib import github
from flask_dance.contrib.github import make_github_blueprint
github_blueprint = make_github_blueprint(client_id='your-client-id',client_secret='your-client-secret')
from app.github import views
github/views.py
#github_blueprint.route("/")
def github_login():
if not github.authorized:
return redirect(url_for('github.login'))
account_info = github.get('/user')
if account_info.ok:
account = account_info.json()
return '<h1>Your Github name is {}'.format(account['login'])
and finally in your main init.py file
from flask import Flask
from flask_fontawesome import FontAwesome
from app.config import Config
fa = FontAwesome()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
fa.init_app(app)
from app.public import public
app.register_blueprint(public)
from app.auth import auth
app.register_blueprint(auth)
from app.github import github_blueprint
app.register_blueprint(github_blueprint, url_prefix='/github_login')
#/github_login=callback url
return app

Flask blueprint for upload of files not getting route

Hey I am building a simple prototype in Flask and I am somehow missing something. The route to the upload is missing otherwise it's the pretty standard tutorial and I have pretty much everything working besides it's not adding the route. I have no clue why the route isn't there the debug simply gives a 404.
My routes in init.py looks like this
#app.route('/hello')
def hello():
return 'Hello, World!'
#app.route('/')
def index():
return render_template('home.html')
from . import uploader
app.register_blueprint(uploader.bp)
from . import db
db.init_app(app)
from . import auth
app.register_blueprint(auth.bp)
return app
And my uploader.py looks like this
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired
from werkzeug.utils import secure_filename
from flaskr.db import get_db
bp = Blueprint('uploader', __name__, url_prefix='/upload')
#bp.route('/upload', methods=('GET', 'POST'))
def upload():
if form.validate_on_submit():
f = form.photo.data
filename = secure_filename(f.filename)
f.save(os.path.join(
app.instance_path, 'photos', filename
))
return redirect(url_for('index'))
return render_template('upload.html', form=form)
I am probably not declaring something the right way but I don't know what.
The issue wasn't in my code but on my system. I fixed this by recloning the repo I already had which was working. I have no clue what broke it.

How to combine Dash and Flask login without using iframe?

I have a flask application, some pages of the app is protected with Flask-Login, I need to add Dash plot to my application, the plot should be on the page which requires authentification. I should add that in addition to Flask Login, I created user access levels using flask session.
I found how to combine Dash with protected page using iframe block, but I would like to try to make dash plot as a separate page (not iframe) with user authentification. If forget about authentification I have no problen to add Dash plot as a separate page in Flask app.
Typically I do the following to check login:
#app.route("/somempage", methods=["GET", "POST"])
#flask_login.login_required
def return_somepage():
active_username = session['user_id']
try:
HOST, USER, PASSWORD, AUTH_PLUGIN =
InitSetup.read_mysql_init_config_file(WORKDIR +
os.path.join(os.getcwd(), "mosreg_webscrap_website.config.txt"))
conn, curs = MySQLOP.create_mysql_connection(HOST, USER, PASSWORD,
AUTH_PLUGIN)
active_user = MySQLOP.retrieve_userinfo_from_db(curs,
active_username)
#now we check value of db colum and decide if user can see the page:
if active_user[0][4]:
#show page
else:
# return user have no rights to see the page template
I successfully added #login_required in my dash app, using the following:
in __init__.py of flask app
app = Flask(__name__, instance_relative_config=False)
# other stuff ...
#app.route("/dash", methods=['POST','GET'])
#login_required
def dash():
return appdash.index()
in dashapp.py
appdash = dash.Dash(__name__, external_stylesheets=external_stylesheets, server=app)
Notice that I did not define a route in the Dash app declaration (i.e. routes_pathname_prefix='/dash'), so Dash App may only be viewed through the route of my Flask app, to which I put #login_required.
#login_required in my case is the one from Flask tutorial
I have manged to do that by using #login_required coupled with an in function condition to check if the user is logged in and authorized.
If not it will redirect him to the login page.
#main_bp.route('/', methods=['GET'])
#login_required
def dashboard():
return protected_view()
Adding more details..
## app.py
import flask_login
from flask_login import login_required
from flask_login import LoginManager
login_manager = LoginManager()
app = Flask(__name__, instance_relative_config=False)
login_manager.init_app(app)
with app.app_context():
# Import parts of our application
from visual import routes, auth
# Register Blueprints
app.register_blueprint(routes.main_bp)
app.register_blueprint(auth.auth_bp)
# Create Database Models
db.create_all()
## routes.py
from flask_login import current_user
from flask import current_app as app
from .assets import compile_auth_assets
from flask_login import login_required
# Blueprint Configuration
main_bp = Blueprint('main_bp', __name__,
template_folder='templates',
static_folder='static')
compile_auth_assets(app)
#main_bp.route('/', methods=['GET'])
#login_required
def dashboard():
return protected_view()
This tutorial should be useful..
https://scotch.io/tutorials/authentication-and-authorization-with-flask-login

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.

Categories