I have a not-logged-in module/blueprint, welcome, and a logged-in blueprint, home. I want a user with a valid session to go to home.index, and a user that is a guest to go to welcome.index. However, I am running into issues because both of those functions route to the same URL, /.
How can I make this functionality possible? I have tried adding:
if(logged_in():
redirect(url_for('home.index'))
to index() in the welcome blueprint, but this of course just causes a circular redirect, because the URL for home.index is the same as welcome.index.
I have also tried to only define welcome.index if logged_in() is true. However, that causes issues because there are other links on the site that link to welcome.index, and if the user is logged in, those cause errors as welcome.index technically no longer exists.
Edit
I am seeing this error AttributeError: 'Blueprint' object has no attribute 'index' from this code:
from flask import Flask, session, g
from modules.welcome import welcome
from modules.home import home as home
from modules.home import index
from modules.welcome import index
app = Flask(__name__)
app.config.from_pyfile('config.cfg')
app.register_blueprint(welcome)
app.register_blueprint(home)
#app.route('/', methods=['GET', 'POST'])
def index():
if 'user_id' in session:
return home.index()
else:
return welcome.index()
Edit #2: Blueprints code
Code in modules/home.py:
from flask import Blueprint, render_template, redirect, url_for, request, session, g
from models.User import User
from helpers.login import *
home = Blueprint('home', __name__)
def index():
return render_template('home/index.html')
Code in modules/welcome.py:
from flask import Blueprint, render_template, redirect, url_for, request, session, g
import md5
from models.User import User
from helpers.login import *
welcome = Blueprint('welcome', __name__)
def index():
alert, email = None, None
if request.method == 'POST' and not logged_in():
email = request.form['email']
password_salt = md5.new(request.form['password']).hexdigest()
user = User.query.filter_by(email=email , password_salt=password_salt).first()
if user is None:
alert = "Wrong username or password!"
else:
session['user_id'] = user.id
return redirect(url_for('home.index'))
return render_template('welcome/index.html', alert=alert, email=email)
#welcome.route('/about')
def about():
return render_template('welcome/about.html')
#welcome.route('/tandp')
def tandp():
return render_template('welcome/tandp.html')
#welcome.route('/logout')
def logout():
session.pop('user_id', None)
return redirect(url_for('welcome.index'))
#welcome.route('/register')
def register():
error = None
return "HI"
Split up your methods, test for logged status and then call the proper function (adding the params you need on each):
from ????? import app
from ????? import logged_in
import home.index
import welcome.index
#app.route('/')
def your_basic_index_view():
if logged_in():
return home.index()
else:
return welcome.index()
Or do the same with a decorator. You won't be able to use a single route pointing conditionally to two different views.
EDIT:
Try the following:
from flask import Flask, session, g
from modules.welcome import welcome
from modules.home import home as home
from modules.home import index as h_index
from modules.welcome import index as w_index
app = Flask(__name__)
app.config.from_pyfile('config.cfg')
app.register_blueprint(welcome)
app.register_blueprint(home)
#app.route('/', methods=['GET', 'POST'])
def index():
if 'user_id' in session:
return h_index()
else:
return w_index()
Related
I was following one of cs50's lectures and writing the same code in vscode as in the lecture. For some reason it works in the course's ide but when it is in vscode on my PC the session forgets the input after redirection. Where is the problem and Is it possible to fix it?
from flask import Flask, render_template, request, redirect, session
from flask_session import Session
app = Flask(__name__)
app.config['SESSION_PERMANENT'] = False
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SECRET_KEY'] = 'randomkey'
Session(app)
#app.route('/')
def tasks():
if 'todos' not in session:
session['todos'] = []
return render_template('tasks.html', todos=session['todos'])
#app.route('/add', methods=["GET", "POST"])
def add():
if request.method == "GET":
return render_template('add.html')
else:
todo = request.form.get('task')
session['todos'].append(todo)
return redirect('/')
New to Flask and Python. I've cloned a github Flask chat app example and am trying to get a referrer URL (i.e. the URL the user was in before going into my app). However, when I run the app locally, the referrer link always come back as None if the request comes from an external URL. If it is sent from within the app, I am getting the right referrer URL.
Here's the relevant bits of code. I've tried looking at previous questions, but couldn't find a solution.
My routing logic:
from flask import session, redirect, url_for, render_template, request
from . import main
from .forms import LoginForm
#main.before_request
def before_request():
print("Ref1:", request.referrer)
print("Ref2:", request.values.get("url"))
#main.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm()
ip_address = request.access_route[0] or request.remote_addr
print("ip_addr:", ip_address)
if form.validate_on_submit():
session['name'] = form.name.data
session['room'] = form.room.data
return redirect(url_for('.chat'))
elif request.method == 'GET':
form.name.data = session.get('name', '')
form.room.data = session.get('room', '')
return render_template('index.html', form=form)
#main.route('/chat')
def chat():
name = session.get('name', '')
room = session.get('room', '')
if name == '' or room == '':
return redirect(url_for('.index'))
return render_template('chat.html', name=name, room=room)
My main app code is:
#!/bin/env python
from app import create_app, socketio
app = create_app(debug=True)
if __name__ == '__main__':
socketio.run(app)
Would really appreciate any advice.
Thanks!
I am trying to build a Dash app which is integrated in Flask app. Everything seems to be working fine but when I try to show logged user in the Dash app it comes out as 'None'.
My app structure is as below:
example/
example/
dashapp/
static/
custom-css.css
templates/
base.html
home.html
login.html
__init__.py
app1.py
forms.py
models.py
routes.py
application.py
config.py
users.db
My Dash app is in app1.py. I've tried several ways to pass current_user but no success. It comes out fine in home.html though. I guess the problem is with my app being in separate file and not in the routes.py.
Here is the code for app.py:
import dash
import dash_html_components as html
from dashapp import application
from flask_login import login_required, current_user
app1 = dash.Dash(__name__, server = application, routes_pathname_prefix = '/app1/', assets_folder = 'static', assets_url_path = '/static')
app1.scripts.config.serve_locally = True
app1.css.config.serve_locally = True
app1.layout = html.Div(
children = '{}'.format(current_user)
)
for view_func in app1.server.view_functions:
if view_func.startswith('/app1/'):
app1.server.view_functions[view_func] = login_required(app1.server.view_functions[view_func])
routes.py code:
from flask import render_template, flash, redirect, url_for, request
from flask_login import login_user, logout_user, current_user, login_required
from werkzeug.urls import url_parse
from dashapp import application, db
from dashapp.forms import LoginForm
from dashapp.models import User
from dashapp import app1
#application.route('/')
#application.route('/home')
#login_required
def home():
return render_template('home.html')
#application.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('home'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('login'))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('home')
return redirect(next_page)
return render_template('login.html', form=form)
#application.route('/app1/')
#application.route('/logout')
def logout():
logout_user()
return redirect(url_for('login'))
Other scripts are pretty much standard and I won't include them in the question unless they are really needed.
Please suggest how to overcome my problem. Thanks!
I managed to fix this with some help. Just in case somebody gets stuck see below updated code.
Added session rows to store current username in routes.py:
from flask import render_template, flash, redirect, url_for, request, session
from flask_login import login_user, logout_user, current_user, login_required
from werkzeug.urls import url_parse
from dashapp import application, db
from dashapp.forms import LoginForm
from dashapp.models import User
from dashapp import app1
#application.route('/')
#application.route('/home')
#login_required
def home():
return render_template('home.html')
#application.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
session['username'] = current_user.username
return redirect(url_for('home'))
form = LoginForm()
if form.validate_on_submit():
session['username'] = form.username.data
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('login'))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('home')
return redirect(next_page)
return render_template('login.html', form=form)
#application.route('/logout')
def logout():
session.pop('username', None)
logout_user()
return redirect(url_for('login'))
And session in the callback for app1.py:
import dash
import dash_html_components as html
from dash.dependencies import Input, Output
from dashapp import application
from flask_login import login_required
from flask import session
app1 = dash.Dash(__name__, server = application, routes_pathname_prefix = '/app1/', assets_folder = 'static', assets_url_path = '/static')
app1.scripts.config.serve_locally = True
app1.css.config.serve_locally = True
app1.layout = html.Div(
children = [
html.Div(id='div2'),
html.Div(id='div3', children = 'xxxx'),
],
)
#app1.callback(
Output('div2', 'children'),
[Input('div3', 'children')])
def update_intervalCurrentTime(children):
return session.get('username', None)
for view_func in app1.server.view_functions:
if view_func.startswith('/app1/'):
app1.server.view_functions[view_func] = login_required(app1.server.view_functions[view_func])
Faced the same problem. I guess the problem is that current user hasn't been initialised when the 'application' registers Dash app. Nice workaround, though seems a bit insecure since Flask sessions are just encoded.
https://github.com/RafaelMiquelino/dash-flask-login/blob/master/app.py - more robust solution in my opinion. It checks current_user in the callbacks and uses contents of the page as Input, so the callbacks are called on page load. You can also skip the protection of the views if user experience isn't an issue.
Here's a simplified example of how I used it:
#app.callback(
Output('graph-wrapper', 'children'),
[Input('page-content', 'children')])
def load_graph(input1):
if current_user.is_authenticated:
return dcc.Graph() # Dash Graph populated with current_user data from db
else:
return ''
I am using flask_login for login and logout for an app but the redirection back to the previous page does not seem to be working. I am using the flask.views and login_required as a decorator for views that require user login. However, when I try to access /path which requires login it redirects to /login and not /login?next=/path which means that request.get.args("next") is None.
I am using login required with flask views in my blueprint as follows:
from flask import Blueprint, render_template, request, redirect, url_for
from flask.views import MethodView
from models import Post
from flask.ext.mongoengine.wtf import model_form
from flask.ext.login import login_required
posts_app = Blueprint('posts_app', __name__, template_folder='templates', static_folder='static', static_url_path='/static')
class ListView(MethodView):
decorators = [login_required]
def get(self):
posts = Post.objects.all()
print posts
return render_template('posts/list.html', posts=posts)
posts_app.add_url_rule('/', view_func=ListView.as_view('list'))
In a separate blueprint I am implementing Authentication:
from flask import Blueprint, render_template, request, current_app, flash, redirect, url_for
from forms import LoginForm, RegisterForm, ForgotForm
from libs.User import User
from flask.ext.login import login_user, login_required, logout_user, confirm_login
from app import login_manager, flask_bcrypt
auth_login = Blueprint('auth_login', __name__, template_folder='templates')
#auth_login.route('/login', methods=["GET", "POST"])
def login():
if request.method == "POST" and "email" in request.form:
email = request.form["email"]
userObj = User()
user = userObj.get_by_email_w_password(email)
if user and user.is_active() and flask_bcrypt.check_password_hash(user.password, request.form["password"]):
remember = request.form.get("remember", "no") == "yes"
if login_user(user, remember=remember):
flash("Logged In!")
return redirect(request.args.get('next') or url_for('index'))
else:
flash("Unable to log you in")
form = LoginForm(request.form)
return render_template('forms/login.html', form=form)
Could anyone familiar with login required decorator offer some advice? Thanks!
Found what what was going wrong. In my authentication Blueprint, I was overriding #login_manager.unauthorized_handler to be
#login_manager.unauthorized_handler
def unauthorized_callback():
return redirect('/login')
whereas I really needed to do:
#login_manager.unauthorized_handler
def unauthorized_callback():
return redirect('/login?next=' + request.path)
What I did was something like this.
#app.after_request
def redirect_to_signin(response):
if response.status_code == 401:
return redirect(APP_HOST + '/auth/signin?next=' + request.url)
Then whenever a user can't be authenticated it just redirects them to the signin page and then exactly back to where they were. Great if you have different host names (i.e. app.website.com & website.com). See other request url fields in this SO answer
I have a question regarding blueprints. I have an app which is structured like this
app
/run.py
/APP
/__init__.py
/VIEWS
/__init__.py
/general.py
/crud.py
this is the code http://pastebin.com/bsHsTGAP
run.py
from overwatch import app
app.run()
__init__.py
from flask import Flask, session, g, render_template, request, redirect, url_for, Response
import websiteconfig as config
from flaskext.principal import Identity, Principal, RoleNeed, UserNeed, \
Permission, identity_changed, identity_loaded
app = Flask(__name__)
app.debug = config.DEBUG
app.secret_key = config.SECRET_KEY
principals = Principal(app)
principals._init_app(app)
#app.errorhandler(404)
def not_found(error):
return render_template('404.html'), 404
#app.errorhandler(403)
def page_not_found(e):
session['redirected_from'] = request.url
return redirect(url_for('crud.login'))
# handle login failed
#app.errorhandler(401)
def page_not_found(e):
return Response('<p>Login failed</p>')
from overwatch.views import general
from overwatch.views import crud
app.register_blueprint(general.mod)
app.register_blueprint(crud.mod)
general.py
from flask import Blueprint, render_template, session, redirect, url_for, \
request, flash, g, Response, jsonify
from flaskext.principal import Identity, Principal, RoleNeed, UserNeed, \
Permission, identity_changed, identity_loaded
from .. import principals
mod = Blueprint('general', __name__)
normal_role = RoleNeed('normal')
normal_permission = Permission(normal_role)
#mod.route('/')
#normal_permission.require(http_exception=403)
def index():
return "YOU'RE IN"
crud.py
from flask import Blueprint, render_template, session, redirect, url_for, \
request, flash, g, Response, jsonify, abort, Response
from mongokit import Connection, Document
from db import user_exists, email_exists, return_attribute, check_credentials
from forms import RegistrationForm, LoginForm
from .. import app
from flaskext.principal import Identity, Principal, RoleNeed, UserNeed, \
Permission, identity_changed, identity_loaded
from general import normal_role, normal_permission
mod = Blueprint('crud', __name__)
#mod.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm(request.form)
error = None
if request.method == 'POST' and form.validate():
if check_credentials(form.username.data,form.password.data):
identity = Identity(form.username.data)
identity_changed.send(app, identity=identity)
return redirect(session['redirected_from'])
else:
return abort(401)
return render_template('login.html', form=form, error=error)
#app.route("/logout/")
def logout():
for key in ['identity.name', 'identity.auth_type', 'redirected_from']:
try:
del session[key]
except:
pass
return Response('<p>Logged out</p>')
#identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
identity.provides.add(normal_role)
Thing is, I seem to be importing a lot of stuff into a lot of stuff. Right now it works. if i go to the index paged, which is handled by general.py blueprint and secured with normal_permission it redirects to /login which is handled by crud.py blueprint and if logged in redirects to index. Again, right now it... works but .. it also feels realllllllllly dirty and unclean and .. bleac... so unlike some of the good code I read :)
Any suggestions are welcome please. If this is not the way to tackle it, I'm willing to learn. I dont want to have some code that .. just works.
Thank you for your time reading this and maybe answering it.
ps. if i pasted too much code here let me know and I'll edit it out.
To access the current application from your blueprint's views, you should use the flask.current_app object, it is a proxy to the current application (and it's what's used in flask extensions for example).
Regarding your code, except the unused imports, it's well organised in my opinion, but i can't tell about the principal part, as i've never used it.