I have built a website using flask (www.csppdb.com). Sometimes when I log in as one user, log out, then login as another user I still see pages from the first user I logged in as. This problem is immediately fixed when the page is refreshed. I think this is called "caching" if I am not mistaken. Is there any way I could disable this on a site wide level so that every page that is visited needs a new refresh?
It would be like sharing your computer with a friend. He logs into Facebook, then logs out. Now you log in on his computer and you see his profile... (awkward). After you refresh the page the problem is fixed.
Here is some of my code. I was using flask-login but I then tried to "roll my own"
from flask.ext.mysql import MySQL
import os
from flask import Flask, request, jsonify, session, url_for, redirect, \
render_template, g, flash
from data import *
from werkzeug import check_password_hash, generate_password_hash
import config
app = Flask(__name__)
mysql = MySQL()
app.config['MYSQL_DATABASE_HOST'] = os.environ['MYSQL_DATABASE_HOST'] if 'MYSQL_DATABASE_HOST' in os.environ else config.MYSQL_DATABASE_HOST
app.config['MYSQL_DATABASE_PORT'] = os.environ['MYSQL_DATABASE_PORT'] if 'MYSQL_DATABASE_PORT' in os.environ else config.MYSQL_DATABASE_PORT
app.config['MYSQL_DATABASE_USER'] = os.environ['MYSQL_DATABASE_USER'] if 'MYSQL_DATABASE_USER' in os.environ else config.MYSQL_DATABASE_USER
app.config['MYSQL_DATABASE_PASSWORD'] = os.environ['MYSQL_DATABASE_PASSWORD'] if 'MYSQL_DATABASE_PASSWORD' in os.environ else config.MYSQL_DATABASE_PASSWORD
app.config['MYSQL_DATABASE_DB'] = os.environ['MYSQL_DATABASE_DB'] if 'MYSQL_DATABASE_DB' in os.environ else config.MYSQL_DATABASE_DB
mysql.init_app(app)
if 'SECRET_KEY' in os.environ: app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
else: app.config['SECRET_KEY'] = os.urandom(24)
def connect_db(): return mysql.connect()
def check_auth():
g.user = None
if 'username' in session:
g.user = get_user(session['username'])
return
return redirect(url_for('login'))
#app.route('/')
def home():
if 'username' in session: return redirect(url_for('main'))
return render_template('home.html')
def connect_db(): return mysql.connect()
#app.teardown_request
def teardown_request(exception):
if exception: print exception
g.db.close()
#app.before_request
def before_request():
print session.keys(), session.values()
print("before request")
print ('username' in session, "in session?")
g.db = connect_db()
g.user = None
if "username" in session:
g.user = get_user(session['username'])
#app.route('/login/', methods=['GET', 'POST'])
def login():
"""Logs the user in."""
if 'username' in session:
return redirect(url_for('main'))
error = None
if request.method == 'POST':
print("login hit")
user = get_user(request.form['username'])
if user is None:
error = 'Invalid username'
print error
elif not check_password_hash(user.password, request.form['password']):
error = 'Invalid password'
print error
else:
flash('You were logged in')
print "logged in"
session['username'] = request.form['username']
g.user = request.form['username']
print error, "error"
return redirect(url_for('main'))
return render_template('login.html', error=error)
Setting the cache to be max-age=0 fixed it.
#app.after_request
def add_header(response):
"""
Add headers to both force latest IE rendering engine or Chrome Frame,
and also to cache the rendered page for 10 minutes.
"""
response.headers['X-UA-Compatible'] = 'IE=Edge,chrome=1'
response.headers['Cache-Control'] = 'public, max-age=0'
return response
To stop browser caching on these sort of pages you need to set some HTTP response headers.
Cache-Control: no-cache, no-store
Pragma: no-cache
Once you do this then the browser wont cache those pages. I dont know how to do this with "flask" so I will leave that as an exercise for you :)
This question shows how to add a response header Flask/Werkzeug how to attach HTTP content-length header to file download
Related
I am currently in the process of building a flask based LAN chatting app (using sqlite3 to store usernames and socketio for messaging) and am having trouble implementing sessions correctly.
I have followed both this guide:
https://www.techwithtim.net/tutorials/flask/sessions/
and read the documentation here https://flask-session.readthedocs.io/en/latest/ but am somehow still not getting my code to work:
In the login page, when the username is posted, I want users to be redirected to the chat-page.html, but this does not occur. Instead they are redirected to the login page, and I cannot figure out why:
from flask import Flask, render_template, request, flash, session, redirect, url_for
#creating the routes
#app.route('/login', methods=["POST", "GET"])
def login_form():
if request.method == "POST":
username = request.form.get("user_name")
session["user"] = username
return redirect(url_for('chat_page'))
else:
if "user" in session:
return redirect(url_for('chat_page'))
return render_template('login.html')
#app.route('/chat-page')
def chat_page():
if "user" in session:
username = session["user"]
return render_template('chat-page.html', Uname=username)
return redirect(url_for('login_form'))
#app.route("/logout")
def logout():
session.pop("user", None)
flash("You have been logged out!")
return redirect(url_for('login_form'))
from flask_session import Session
app = Flask(__name__)
Session(app)
When I tried to debug your code, I ran into issues with the secret key. I don't know how or where you set it or call your app, but here is my complete code that worked. They key might be to set app.config['SESSION_TYPE'] = 'filesystem'. I used this answer to solve it.
from flask import Flask, render_template, request, flash, session, redirect, url_for
from flask_session import Session
app = Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SECRET_KEY'] = 'secret key'
Session(app)
# creating the routes
#app.route('/login', methods=["POST", "GET"])
def login_form():
if request.method == "POST":
username = request.form.get("user_name")
session["user"] = username
return redirect(url_for('chat_page'))
else:
if "user" in session:
return redirect(url_for('chat_page'))
return render_template('login.html')
#app.route('/chat-page')
def chat_page():
if "user" in session:
return '<div>chat page!</div>'
return redirect(url_for('login_form'))
#app.route("/logout")
def logout():
session.pop("user", None)
flash("You have been logged out!")
return redirect(url_for('login_form'))
app.run(debug=True)
What I'm saying is, your redirect logic is completely fine. The issue must be with the session.
Im writing a dummy website for class and I'm having trouble connecting my Heroku Database to my app that's local for now until I push to Heroku.
I'm not sure what the proper way to do this, and I've searched many videos/forums and I can't seem to get a straight answer from them. Ill post some of my code below. In dbconnect.py where the insert the heroku database credentials, like the URI, host, etc?
#app.py
from flask import Flask, render_template, redirect, url_for, request, session, flash
from functools import wraps
app = Flask(__name__)
app.secret_key = "Gundam"
# login required decorator
def login_required(f):
#wraps(f)
def wrap(*args, **kwargs):
if 'logged_in' in session:
return f(*args, **kwargs)
else:
flash('You need to login first.')
return redirect(url_for('login_page'))
return wrap
#app.route('/')
def homepage():
return render_template("main.html")
#app.route('/dashboard/')
#login_required
def dashboard():
return render_template("dashboard.html")
#app.errorhandler(404)
def page_not_found(e):
return render_template("404.html")
#app.route('/login/', methods=["GET", "POST"])
def login_page():
error = ''
try:
if request.method == "POST":
attempted_username = request.form['username']
attempted_password = request.form['password']
if attempted_username == "admin" and attempted_password == "password":
session['logged_in'] = True
flash('You were just logged in!')
return redirect(url_for('dashboard'))
else:
error = "Invalid Username or Password."
return render_template("login.html", error=error)
except Exception as e:
return render_template("login.html", error=error)
#app.route('/logout/')
def logout():
session.pop("logged_in", None)
return redirect(url_for('homepage'))
if __name__ == '__main__':
app.run(debug = True)
dbconnect.py
import os
import psycopg2
import urlparse
urlparse.uses_netloc.append("postgres")
url = urlparse.urlparse(os.environ[""])
conn = psycopg2.connect(
database=url.path[1:],
user=url.username,
password=url.password,
host=url.hostname,
port=url.port
)
You have to install the postgres database addon in heroku first. Run the heroku toolbelt in your computer and enter the command heroku addons:create heroku-postgresql:hobby-dev. Hobby-dev is the free version.
Once Heroku Postgres has been added a DATABASE_URL setting will be available in the app configuration and will contain the URL used to access the newly provisioned Heroku Postgres service. Use the value as your database uri. The app configuration can be accessed from your dashboard. Under settings, click Reveal config vars. You can also use the toolbelt command. See heroku config -h.
So now you can do:
url = urlparse.urlparse(os.environ["DATABASE_URL"])
For more details see https://devcenter.heroku.com/articles/heroku-postgresql
Just use the following code snippet on your python app. That should do the trick.
import os
import psycopg2
DATABASE_URL = os.environ['DATABASE_URL']
conn = psycopg2.connect(DATABASE_URL, sslmode='require')
Does Flask have any built-in support for user login/logout functionality? I've found this add-on project, but it only seems to provide the pieces to build your own login system. It doesn't seem to be a complete system.
I'm new to Flask, and I'm coming from Django where this is all built-in, so I'm finding it a little baffling that this basic functionality is missing.
Using some incomplete examples I've found, I'm trying to implement an index page that redirects to a login page for anonymous users, and after a successful login, redirects page to the index page. This is what I currently have implemented as a "hello world" login/logout example:
#!/usr/bin/env python
import flask
from flask import Flask, Response, render_template
from flask.ext.login import LoginManager, UserMixin, login_required, login_user, logout_user
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
class User(UserMixin):
# proxy for a database of users
user_database = {
"JohnDoe": ("JohnDoe", "John"),
"JaneDoe": ("JaneDoe", "Jane"),
}
def __init__(self, username, password):
self.id = username
self.password = password
#property
def name(self):
return self.id
#classmethod
def get(cls, id):
ret = cls.user_database.get(id)
if ret is not None:
return cls(*ret)
#login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
#login_manager.request_loader
def load_user(request):
token = request.headers.get('Authorization')
if token is None:
token = request.args.get('token')
if token is not None:
username,password = token.split(":") # naive token
user_entry = User.get(username)
if (user_entry is not None):
user = User(user_entry[0],user_entry[1])
if (user.password == password):
return user
return None
#app.route("/",methods=["GET"])
#login_manager.request_loader
def index():
if load_user(flask.request):
return render_template('index.html')
else:
return flask.redirect(flask.url_for('login'))
#return Response(response="Hello World!",status=200)
#app.route('/login', methods=['GET', 'POST'])
def login():
if flask.request.method == 'GET':
return '''
<form action='login' method='POST'>
<input type='text' name='email' id='email' placeholder='email'></input>
<input type='password' name='pw' id='pw' placeholder='password'></input>
<input type='submit' name='submit'></input>
</form>
'''
email = flask.request.form['email']
user = User.get(email)
if user and flask.request.form['pw'] == user.password:
login_user(user)
return flask.redirect(flask.url_for('index'))
return 'Bad login'
#app.route("/logout")
#login_required
def logout():
logout_user()
return flask.redirect(flask.url_for('index'))
if __name__ == '__main__':
app.config["SECRET_KEY"] = "ITSASECRET"
app.run(port=5000, debug=True)
However, it doesn't work, because even though it seems to login successfully, when it redirects to the index page, it can't lookup the user from the session and redirects back to the login page. What am I doing wrong?
Flask-Login is a very basic login manager that is built upon with a few other user management frameworks. I have been using Flask-User in production for over 1.5 years with about 30K users and have not had any problems with it (which uses flask-login under the hood). The maintainer is active and has responded to my issues in a timely manner.
It handles user login, registration, lost password, and even email confirmations if so desired. It comes with some pre-built forms if you don't want to mess with that but is easily customizable if you do.
Sounds like maybe when login_user gets called the login is not being persisted. I would make sure however you're doing your sessions is actually storing the login.
I just finished the Flask basic tutorial (here) and even though I've done completed every step, when I am trying
python flaskr.py
what I get is a 404 Not Found error saying
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
here is the code inside the file
import os
import sqlite3
from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash
#create app
app = Flask(__name__)
app.config.from_object(__name__)
#load default conf and override config from an env var
app.config.update(dict(
DATABASE=os.path.join(app.root_path, 'flaskr.db'),
DEBUG=True,
SECRET_KEY = 'dev key',
USERNAME = 'admin',
PASSWORD = 'admin'
))
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
def connect_db():
"""connect to the specific db"""
rv = sqlite3.connect(app.config['DATABASE'])
rv.row_factory = sqlite3.Row
return rv
if __name__== '__main__':
app.run()
def get_db():
"""opens a new db connection if there is none yet for the cyrrent app"""
if not hasattr(g, 'sqlite_db'):
g.sqlite_db = connect_db()
return g.sqlite_db
#app.teardown_appcontext
def close_db(error):
"""closes the db again at the end of the request."""
if hasattr(g, 'sqlite_db'):
g.sqlite_db.close()
def init_db():
with app.app_context():
db = get_db()
with app.open_resource('schema.sql', mode='r') as f:
db.cursor().executescript(f.read())
db.commit()
#app.route('/')
def show_entries():
db_get_db()
cur = db.execute('select title, text from entries order by id desc')
entries = cur.fetchall()
return render_template('show_entries.html', entries=entries)
#app.route('/add', methods=['POST'])
def add_entry():
if not session.get('logged_in'):
abort(401)
db = get_db()
db.execute('insert into entries (title, text) values (?,?)',
[request.form['title'], request.form['text']])
db.commit()
flash('New entry was successfully posted')
return redirect(url_for('show_entries'))
#app.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != app.config['USERNAME']:
error = 'Invalid username'
elif request.form['password'] != app.config['PASSWORD']:
error = 'Invalid password'
else:
session['logged_in'] = True
flash('You were logged in')
return redirect(url_for('show_entries'))
return render_template('login.html', error=error)
#app.route('/logout')
def logout():
session.pop('logged_in', None)
flash('You were logged out')
return redirect(url_for('show_entries'))
This is the console message I get (plus 3 attempts to refresh page):
user#user:~/Flask/flaskr$ python flaskr.py
* Running on http://127.0.0.1:5000/
* Restarting with reloader
127.0.0.1 - - [19/Aug/2014 15:23:40] "GET / HTTP/1.1" 404 -
127.0.0.1 - - [19/Aug/2014 15:23:41] "GET / HTTP/1.1" 404 -
127.0.0.1 - - [19/Aug/2014 15:23:42] "GET / HTTP/1.1" 404 -
Any suggestions on what might be going wrong?
You put your app.run() call too early:
if __name__== '__main__':
app.run()
This is executed before any of your routes are registered. Move these two lines to the end of your file.
Next, you have the first line in show_entries() is incorrect:
def show_entries():
db_get_db()
There is no db_get_db() function; this should be db = get_db() instead.
Happened to me also while following Flask Application Setup here . The error was gone after appending a trailing slash at the end of the route.
#app.route('/hello')
def hello():
return 'Hello, World!'
was changed to
#app.route('/hello/')
def hello():
return 'Hello, World!'
and the problem was solved. Hope it helps for anyone who is searching for a problem like this.
You should delete your cache and reload the page.
Press "Ctrl" + "Shift" + "i" to open the developer tools
Right-click on the "reload-icon" (⟳) located to the left of the URL bar
Select "Clear cache and refresh completely"
I have a sample web application (flask with flask-login running on heroku) at this URL: http://twittaclone.herokuapp.com.
When I run it on my localhost the login functionality works fine. When I push to heroku it freaks out and does not allow users to login (it does allow user registration). Database modifications are being made.
Why would flask login not work on heroku?
app = Flask(__name__)
mysql = MySQL()
app.config['MYSQL_DATABASE_HOST'] = os.environ['MYSQL_DATABASE_HOST'] if 'MYSQL_DATABASE_HOST' in os.environ else config.MYSQL_DATABASE_HOST
app.config['MYSQL_DATABASE_PORT'] = os.environ['MYSQL_DATABASE_PORT'] if 'MYSQL_DATABASE_PORT' in os.environ else config.MYSQL_DATABASE_PORT
app.config['MYSQL_DATABASE_USER'] = os.environ['MYSQL_DATABASE_USER'] if 'MYSQL_DATABASE_USER' in os.environ else config.MYSQL_DATABASE_USER
app.config['MYSQL_DATABASE_PASSWORD'] = os.environ['MYSQL_DATABASE_PASSWORD'] if 'MYSQL_DATABASE_PASSWORD' in os.environ else config.MYSQL_DATABASE_PASSWORD
app.config['MYSQL_DATABASE_DB'] = os.environ['MYSQL_DATABASE_DB'] if 'MYSQL_DATABASE_DB' in os.environ else config.MYSQL_DATABASE_DB
mysql.init_app(app)
if 'SECRET_KEY' in os.environ: app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
else: app.config['SECRET_KEY'] = os.urandom(24)
def connect_db(): return mysql.connect()
###
# Routing for your application.
###
login_manager = LoginManager()
login_manager.login_view = "login"
#login_manager.user_loader
def load_user(username):
g.db = connect_db()
return get_user(username)
login_manager.init_app(app)
#app.route('/')
def home(): return render_template('home.html')
def connect_db(): return mysql.connect()
#app.before_request
def before_request():
g.user = current_user
g.db = connect_db()
#app.teardown_request
def tear_down(exception):
g.db.close()
#app.route('/main/')
#login_required
def main():
print("in main")
tweets, user = get_main()
follower_count, followee_count = get_follower_info(g.user.username)
return render_template('main.html', user=user, tweets=tweets, followercount = follower_count, followeecount = followee_count)
#app.route('/login/', methods=['GET', 'POST'])
def login():
"""Logs the user in."""
if request.method == 'GET':
if current_user is user_logged_in: logout_user()
error = None
if request.method == 'POST':
user = get_user(request.form['username'])
if user is None:
error = 'Invalid username'
elif not check_password_hash(user.password, request.form['password']):
error = 'Invalid password'
else:
flash('You were logged in')
login_user(user)
return redirect(url_for('main'))
return render_template('login.html', error=error)
Many years later but this solves your issue: Heroku gunicorn flask login is not working properly
Updated Procfile:
web: gunicorn app:app --preload
Documentation on preloading for gunicorn: https://docs.gunicorn.org/en/stable/settings.html
I'm pretty sure that Heroku uses PostgreSQL. Try referring to their documents https://devcenter.heroku.com/articles/heroku-postgresql