Current user in Flask context processor - python

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.

Related

Flask session variables

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.

Check Flask cookie for user_id and obtain User if it exists or redirect if not

What I'm after is something like what the #login_required decorators accomplish but I'm not sure if a custom decorator would allow me to pass the User option back to my route function. There are several pages in my app that require the user to be logged in to access them so I am looking for the most efficient way/least code to copy into each access-restricted route that will verify they have a user_id in their cookie (logged in), cache get/query for their User object using the user_id, and carry on with the route function, else redirect to the login page if user_id is not present.
What I was hoping to do was something like:
#noteBP.route('/note', methods=['GET', 'POST'])
def new_note():
user_details = return_user_details_if_logged_in_else_redirect_to_login_url(next=request.url)
...
And that function would check for the user_id in the session cookie and send back the User object or redirect to the login page:
def return_user_details_if_logged_in_else_redirect_to_login_url(next=None):
user_id = session.get('user_id')
if user_id:
user_details = user_util.get_user_details_by_id(user_id)
return user_details
else:
return redirect(url_for('usersBP.login', next=next))
Turns out, redirect does not work the same way as Abort where it gets called even if you are inside another function so now I have do do additional processing back in the route function to check:
user_details = return_user_details_if_logged_in_else_redirect_to_login_url(next=request.url)
if not user_details:
return redirect(redirect_url)
I'm looking to avoid having to paste this chunk of code at the top of every access-restricted route. Is there a more efficient way/DRY approach to do this? if with a decorator, how do I get the user_details into the route function?
If you want to redirect in a function called inside a view, raise a RequestRedirect. If you want to redirect in a decorator, check if the user is not logged in and return a redirect rather than the actual view (or use the previous function to raise the redirect).
import functools
from flask import url_for, redirect, g
from werkzeug.routing import RequestRedirect
def require_login():
if g.user is None:
raise RequestRedirect(url_for('login'))
def login_required(view):
#functools.wraps(view)
def wrapped_view(**kwargs):
require_login()
# or
# if g.user is None:
# return redirect(url_for('login'))
return view(**kwargs)
return wrapped_view
#app.route('/secret2')
#login_required
def secret1():
return 'secret 1'
#app.route('/secret2')
def secret2():
require_login()
return 'secret 2'
Populate g.user in a before_request handler.
from flask import session
#app.before_request
def load_user():
g.user = None
if 'user_id' in session:
# use whatever caching logic you want here.
g.user = User.query.get(session['user_id'])
Populate session['user_id'] in your login view.
#app.route('/login')
def login():
if request.method == 'POST':
user = User.query.filter_by(username=request.form['username']).first()
if user and user.check_password(request.form['password']:
session['user_id'] = user.id
return redirect(url_for('index'))
return render_template('login.html')
Now you can access g.user from any route, without passing it explicitly. If you do want to pass it explicitly, modify login_required.
def require_login():
if g.user is None:
raise RequestRedirect(url_for('login'))
return g.user
def login_required(view):
#functools.wraps
def wrapped_view(**kwargs):
user = require_login()
return view(user, **kwargs)
return wrapped_view
#app.route('/secret')
def secret(user):
return 'user {} is logged in'.format(user.id)
Give that all of this except passing the user is part of Flask-Login, you should really reconsider using Flask-Login instead of trying to maintain your own solution.
A decorator is a function that wraps and replaces another function. Since the original function is replaced, you need to remember to copy the original function’s information to the new function. Use functools.wraps() to handle this for you.
This example assumes that the login page is called 'login' and that the current user is stored in g.user and is None if there is no-one logged in.
from functools import wraps
from flask import g, request, redirect, url_for
def login_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if g.user is None:
return redirect(url_for('login', next=request.url))
return f(*args, **kwargs)
return decorated_function
To use the decorator, apply it as innermost decorator to a view function. When applying further decorators, always remember that the route() decorator is the outermost.
#app.route('/secret_page')
#login_required
def secret_page():
pass
Note:
The next value will exist in request.args after a GET request for the login page. You’ll have to pass it along when sending the POST request from the login form. You can do this with a hidden input tag, then retrieve it from request.form when logging the user in.
<input type="hidden" value="{{ request.args.get('next', '') }}"/>

python/flask find unique ids for each website visitor [duplicate]

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'])

python flask do something at each route

I have a flask app where I have to delete and update user information, like this (simplified):
#app.route('/<user>')
def show_user(user):
""" Show user information """
render_template('show_user.html')
#app.route('/delete/<user>')
def delete_user(user):
""" Delete user from database """
delete_user_from_db(user)
return redirect(url_for('show_users', user=user)
#app.route('/update/<user>', method=["POST"])
def update_user(user):
""" Update user information """
update(user, stuff_from_POST)
return redirect(url_for('show_users', user=user)
For each of these methods I need to verify whether the user specified in the URL is really a valid user, so I'd do something like this at the beginning of all those functions:
if user not in mydb:
do(something)
abort(404)
This is rather cumbersome, and since I will be having more functions that depend on the user to be valid, I was wondering if it were possible to wrap that block in another function that gets automatically executed when those routes are called.
Thanks in advance.
Use the before_request hook function see docs
#app.before_request
def before_request():
if user not in mydb:
do(something)
abort(404)
Edit:
I tried this
from flask import Flask, request
app = Flask(__name__)
db = ['paul', 'steve', 'anna']
#app.before_request
def before_request():
if request.endpoint in ['show_user', 'delete_user', 'update_user']:
user = request.path[request.path.rfind('/') + 1:]
if user not in db:
return 'user not found', 404
#app.route('/<user>')
def show_user(user):
""" Show user information """
return 'hello %s' % user
#app.route('/other')
def show_other():
""" Demonstrates other routes are not effected by before_request """
return 'other stuff'
if __name__ == "__main__":
app.run()
It's not actually as neat as I hoped but it works.
It's a little unfortunate to have to maintain which endpoints do what in the before_request function. If you were doing REST you might be able to merge these endpoints into one and simply use different http methods for each action.

Layout bug with my templates

On all my template rendering for a particular app, the output ends with None:
...</html>None
This must be a bug and probably in my code and I've spent days trying to track it down. There's nothing special about my app and this bug appears on every page I use template rendering, whether I use a seperate template engine or not. There is nothing special about my code:
class Objectives(NewBaseHandler):
#user_required
def get(self):
user = auth_models.User.get_by_id(long(self.auth.get_user_by_session()['user_id']))
if user:
self.render_template('objectives.html', {'user': user})
else:
self.render_template('/', {})
class NewBaseHandler(BaseHandler):
"""
........BaseHandler for all requests
........Holds the auth and session properties so they are reachable for all requests
...."""
def dispatch(self):
"""
............Save the sessions for preservation across requests
........"""
# self.session_store = sessions.get_store(request=self.request)
# if self.request.host.find('localhost') > 0: # for a Swedish domain that uses Swedish
# or lang = os.environ.get("HTTP_ACCEPT_LANGUAGE")
i18n.get_i18n().set_locale('sv')
lang_code_get = self.request.get('hl', None)
if lang_code_get:
#self.session['i18n_language'] = lang_code_get
i18n.get_i18n().set_locale(lang_code_get)
try:
response = super(NewBaseHandler, self).dispatch()
self.response.write(response)
finally:
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def auth(self):
return auth.get_auth()
#webapp2.cached_property
def session_store(self):
return sessions.get_store(request=self.request)
#webapp2.cached_property
def auth_config(self):
"""
............Dict to hold urls for login/logout
........"""
return {'login_url': self.uri_for('login'),
'logout_url': self.uri_for('logout')}
class BaseHandler(webapp2.RequestHandler):
#webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(app=self.app)
def render_template(self, file, template_args):
path = os.path.join(os.path.dirname(__file__), 'templates',
file)
self.response.out.write(template.render(path, template_args))
def render_jinja(self, filename, **template_args):
self.response.write(self.jinja2.render_template(filename,
**template_args))
How can I check where the output None is coming from? It's probably not coming from the template and it doesn't seem to be coming from the handlers and there is no other code.
Thank you
In Objectives.get() you must return a value. Since you don't do this Python assumes the result is None. This value you get in NewBaseHandler.dispatch() when calling to base dispatch implementation and then write it to response.
If I get your app correctly returning empty string in get method will solve the problem.

Categories

Resources