I'm writing a small web app with flask. I have a problem with session variables when two users (under the same network) try to use app.
This is the code:
import os
from flask import Flask, request, render_template, g, session
from random import randint
def random():
session['number'] = randint(0,4)
return None
#app.route('/')
def home():
if not session.get('logged_in'):
return render_template('login.html')
else:
return check()
#app.route('/login', methods = ['GET', 'POST'])
def login():
global username
username = request.form['username']
session['logged_in'] = True
session['username'] = username
return check()
#app.route('/check', methods=['GET', 'POST'])
def check():
random()
return render_template('file.html', number = session['number'], user = session['username'])
if __name__ == "__main__":
app.secret_key = ".."
app.run(host = '0.0.0.0',port = 3134, debug=False)
In file.html there is a button type "submit" that call '/check' route.
The problem is when two users use app at same time because the variable 'number' is equal to variable 'number' of the last user that push the button... there isn't indipendence between two sessions.
I would that the two users has two indipendence sessions, like if they use my app in local host.
Using randint(0,4) to generate number means that they will sometimes be equal for different users. To generate unique number every time use uuid:
from uuid import uuid4
def random():
session['number'] = str(uuid4())
return None
or generator:
import itertools
consequent_integers = itertools.count()
def random():
session['number'] = consequent_integers.next()
return None
So you do something like this. It is not tested but should work. You retrieve the current username and the numbers dictionary from the session variable. Check if there is an existing value for the current username. If not create a random number and save it in the session. Else just use the saved value.
#app.route('/check', methods=['GET', 'POST'])
def check():
# retrieve current username and numbers from your session
username = session['username']
numbers = session.get('numbers', {})
# if no number is existing create a new one and save it to session
if username not in numbers:
number = randint(0,4)
numbers['username'] = number
session['numbers'] = numbers
else:
number = numbers['username']
return render_template('file.html', number=number, user=username)
The reason that the session variable is the same for the two users is likely because you're using the same computer / browsing account. It's just like how Facebook remembers your login status after you open a new Facebook tab.
I advise you to get another computer or use a different Chrome Account (top right of Google Chrome) to test out for different users.
Related
I'm using the context_processor decorator in Flask to define a variable, but I want the value of the variable to be dependent on the current user. I am using Turbo-Flask so that the variable's value automatically updates every few seconds. These are my codes:
def generate_dataframe(user):
if 'id' not in dir(user):
return pd.DataFrame()
return pd.DataFrame(['User Loaded'])
#app.context_processor
def inject_load():
return {'df': generate_dataframe(current_user)}
#turbo.user_id
def get_user_id():
if current_user.is_authenticated:
return current_user.id
else:
return None
#app.before_first_request
def before_first_request():
threading.Thread(target=update_table).start()
def update_feed():
with app.app_context():
while True:
time.sleep(1)
turbo.push(turbo.replace(render_template('feed.html'),'feed'))
When I checked, df is always an empty dataframe even if I'm already logged in.
I want it to be similar to a news feed where the page will automatically refresh/update based on your friends' posts, for example.
I have to design a web-app that provides Flask services and Dash services. For example I would like to create a login in Flask, combined with a Dash application. The problem is that I can't bind the flask login with dash. I would need a method like '#require_login' that filters access to even Dash services.
The code is as follows:
app_flask = Flask(__name__)
app_flask.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////login.db'
app_flask.config['SECRET_KEY'] = 'thisissecret'
db = SQLAlchemy(app_flask)
login_manager = LoginManager()
login_manager.init_app(app_flask)
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(30), unique=True)
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
#app_flask.route('/')
def index():
user = User.query.filter_by(username='admin').first()
login_user(user)
return 'You are now logged in!'
#app_flask.route('/logout')
#login_required
def logout():
logout_user()
return 'You are now logged out!'
#app_flask.route('/home')
#login_required
def home():
return 'The current FLASK user is ' + current_user.username
# TODO how to add login_required for dash?
app_dash = Dash(server=app_flask, url_base_pathname='/dash/')
app_dash.layout = html.H1('MY DASH APP')
if __name__ == '__main__':
app_dash.run_server(debug=True)
This line, app_dash = Dash(server=app_flask, url_base_pathname='/dash/'), creates new view_functions in app_flask identified by its url_base_pathname.
You can debug and inspect the value of app_flask.view_functions before and after the creation of app_dash.
Now that we know which view_functions are created by app_dash, we can apply login_required to them manually.
for view_func in app_flask.view_functions:
if view_func.startswith(app_dash.url_base_pathname):
app_flask.view_functions[view_func] = login_required(app_flask.view_functions[view_func])
The `app_dash` endpoints will now be protected.
It would be better if you block all requests by using #app.before_request, and only allow the request if logged in or if the endpoint is marked as public.
def check_route_access():
if request.endpoint is None:
return redirect("/login")
func = app.view_functions[request.endpoint]
if (getattr(func, "is_public", False)):
return # Access granted
# check if user is logged in (using session variable)
user = session.get("user", None)
if not user:
redirect("/login")
else:
return # Access granted```
Now all endpoints will be checked, even dash app endpoints.
Add this decorator named public_route:
def public_route(function):
function.is_public = True
return function
And add the decorator to the public methods, like login, error pages etc.
#public_route
#app.route("/login")
def login():
# show/handle login page
# call did_login(username) when somebody successfully logged in
def did_login(username):
session["user"] = username
This way you never need the #login_required anymore because all endpoints require login unless stated otherwise by #public_route.
A solution : session from flask (work with cookie)
from flask import session
it's an exemple :
#login_manager.user_loader
def load_user(user_id):
# I think here it's good
session["uid"] = user_id
return User.query.get(int(user_id))
# TODO how to add login_required for dash?
if "uid" in session :
app_dash = Dash(server=app_flask, url_base_pathname='/dash/')
app_dash.layout = html.H1('MY DASH APP')
I'm very new to Flask and don't have much experience in Python.
As required by my project, I need to do certain processing in the backend (through Python script) using the input given by user and show the end results to the user afterwards, whenever he logs in.
A little description as to what my project does - Whenever the user logs in, he/she enters any Twitter handle in html form and the python script gets certain details regarding that handle (number of similar users, followers, no. of tweets, etc.) and this process takes approximately 5-6 minutes. Currently, I am processing and storing all this data in CSVs and then showing the results in HTML.
Now, I don't want my user to wait for those 5-6 minutes to view the results and thus, want the processed results to be stored in the database so that the user can view them whenever he logs in, say even after 1-2 hours.
How should I implement this in Flask, by using SQLAlchemy? I already have made the database for login screen as below, but don't know how to go about this.
from flask import Flask, render_template, session
from sqlalchemy import create_engine
engine = create_engine('sqlite:///tutorial.db', echo=True)
app = Flask(__name__)
#app.route('/')
def home():
if not session.get('logged_in'):
return render_template('login.html')
else:
return render_template('test.html')
#app.route('/login', methods=['POST'])
def do_admin_login():
error = False
POST_USERNAME = str(request.form['username'])
POST_PASSWORD = str(request.form['password'])
Session = sessionmaker(bind=engine)
s = Session()
query = s.query(User).filter(User.username.in_([POST_USERNAME]),
User.password.in_([POST_PASSWORD]))
data = query.first()
if data:
session['logged_in'] = True
flash('Login success')
return render_template('test.html')
else:
flash('Login failed!')
return render_template('login.html',error=True)
#app.route("/logout")
def logout():
session['logged_in'] = False
return home()
The test.html mentioned above takes the twitter handle as input from the user through html form.
I want to build a simple webapp as part of my learning activity. Webapp is supposed to ask for user to input their email_id if it encounters a first time visitor else it remembers the user through cookie and automatically logs him/her in to carry out the functions.
This is my first time with creating a user based web app. I have a blue print in my mind but I am unable to figure out how to implement it. Primarily I am confused with respect to the way of collecting user cookie. I have looked into various tutorials and flask_login but I think what I want to implement is much simpler as compared to what flask_login is implementing.
I also tried using flask.session but it was a bit difficult to understand and I ended up with a flawed implementation.
Here is what I have so far (it is rudimentary and meant to communicate my use case):
from flask import render_template, request, redirect, url_for
#app.route("/", methods= ["GET"])
def first_page():
cookie = response.headers['cookie']
if database.lookup(cookie):
user = database.get(cookie) # it returns user_email related to that cookie id
else:
return redirect_url(url_for('login'))
data = generateSomeData() # some function
return redirect(url_for('do_that'), user_id, data, stats)
#app.route('/do_that', methods =['GET'])
def do_that(user_id):
return render_template('interface.html', user_id, stats,data) # it uses Jinja template
#app.route('/submit', methods =["GET"])
def submit():
# i want to get all the information here
user_id = request.form['user_id']# some data
answer = request.form['answer'] # some response to be recorded
data = request.form['data'] # same data that I passed in do_that to keep
database.update(data,answer,user_id)
return redirect(url_for('/do_that'))
#app.route('/login', methods=['GET'])
def login():
return render_template('login.html')
#app.route('/loggedIn', methods =['GET'])
def loggedIn():
cookie = response.headers['cookie']
user_email = response.form['user_email']
database.insert(cookie, user_email)
return redirect(url_for('first_page'))
You can access request cookies through the request.cookies dictionary and set cookies by using either make_response or just storing the result of calling render_template in a variable and then calling set_cookie on the response object:
#app.route("/")
def home():
user_id = request.cookies.get('YourSessionCookie')
if user_id:
user = database.get(user_id)
if user:
# Success!
return render_template('welcome.html', user=user)
else:
return redirect(url_for('login'))
else:
return redirect(url_for('login'))
#app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
# You should really validate that these fields
# are provided, rather than displaying an ugly
# error message, but for the sake of a simple
# example we'll just assume they are provided
user_name = request.form["name"]
password = request.form["password"]
user = db.find_by_name_and_password(user_name, password)
if not user:
# Again, throwing an error is not a user-friendly
# way of handling this, but this is just an example
raise ValueError("Invalid username or password supplied")
# Note we don't *return* the response immediately
response = redirect(url_for("do_that"))
response.set_cookie('YourSessionCookie', user.id)
return response
#app.route("/do-that")
def do_that():
user_id = request.cookies.get('YourSessionCookie')
if user_id:
user = database.get(user_id)
if user:
# Success!
return render_template('do_that.html', user=user)
else:
return redirect(url_for('login'))
else:
return redirect(url_for('login'))
DRYing up the code
Now, you'll note there is a lot of boilerplate in the home and do_that methods, all related to login. You can avoid that by writing your own decorator (see What is a decorator if you want to learn more about them):
from functools import wraps
from flask import flash
def login_required(function_to_protect):
#wraps(function_to_protect)
def wrapper(*args, **kwargs):
user_id = request.cookies.get('YourSessionCookie')
if user_id:
user = database.get(user_id)
if user:
# Success!
return function_to_protect(*args, **kwargs)
else:
flash("Session exists, but user does not exist (anymore)")
return redirect(url_for('login'))
else:
flash("Please log in")
return redirect(url_for('login'))
return wrapper
Then your home and do_that methods get much shorter:
# Note that login_required needs to come before app.route
# Because decorators are applied from closest to furthest
# and we don't want to route and then check login status
#app.route("/")
#login_required
def home():
# For bonus points we *could* store the user
# in a thread-local so we don't have to hit
# the database again (and we get rid of *this* boilerplate too).
user = database.get(request.cookies['YourSessionCookie'])
return render_template('welcome.html', user=user)
#app.route("/do-that")
#login_required
def do_that():
user = database.get(request.cookies['YourSessionCookie'])
return render_template('welcome.html', user=user)
Using what's provided
If you don't need your cookie to have a particular name, I would recommend using flask.session as it already has a lot of niceties built into it (it's signed so it can't be tampered with, can be set to be HTTP only, etc.). That DRYs up our login_required decorator even more:
# You have to set the secret key for sessions to work
# Make sure you keep this secret
app.secret_key = 'something simple for now'
from flask import flash, session
def login_required(function_to_protect):
#wraps(function_to_protect)
def wrapper(*args, **kwargs):
user_id = session.get('user_id')
if user_id:
user = database.get(user_id)
if user:
# Success!
return function_to_protect(*args, **kwargs)
else:
flash("Session exists, but user does not exist (anymore)")
return redirect(url_for('login'))
else:
flash("Please log in")
return redirect(url_for('login'))
And then your individual methods can get the user via:
user = database.get(session['user_id'])
I have a website that needs to be rebranded depending on the URL that a visitor comes in on. For the most part, the content is the same but the CSS is different. I'm brand new to flask and relatively new to session cookies, but I think the best way to do this is to create a session cookie containing a "client" session variable. Then, depending on the client (brand), I can append a specific css wrapper to a template.
How can I access URL params and set one of the param values to a session variable? For example, if a visitor comes in on www.example.com/index?client=brand1, then I'd like to set session['client'] = brand1.
My app.py file:
import os
import json
from flask import Flask, session, request, render_template
app = Flask(__name__)
# Generate a secret random key for the session
app.secret_key = os.urandom(24)
#app.route('/')
def index():
session['client'] =
return render_template('index.html')
#app.route('/edc')
def edc():
return render_template('pages/edc.html')
#app.route('/success')
def success():
return render_template('success.html')
#app.route('/contact')
def contact():
return render_template('pages/contact.html')
#app.route('/privacy')
def privacy():
return render_template('pages/privacy.html')
#app.route('/license')
def license():
return render_template('pages/license.html')
#app.route('/install')
def install():
return render_template('pages/install.html')
#app.route('/uninstall')
def uninstall():
return render_template('pages/uninstall.html')
if __name__ == '__main__':
app.run(debug=True)
You could do so in a #flask.before_request decorated function:
#app.before_request
def set_client_session():
if 'client' in request.args:
session['client'] = request.args['client']
set_client_session will be called on each incoming request.