Heroku Postgres Database with Flask and Python - python

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

Related

Flask session redirects to login page

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.

Problem with Flask Blueprints, canĀ“t remove the /home route, or app crashes

Hi there I'm creating a Flask web app and now I have to create more cruds, so I decided to modularize the app using Blueprints.
I have a Login function on main.py that allows me to enter the app interface:
app.route('/', methods=['GET', 'POST'])
def login():
# Output message if something goes wrong...
msg = ''
# Check if "username" and "password" POST requests exist (user submitted form)
if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
# Create variables for easy access
username = request.form['username']
password = request.form['password']
# Check if account exists using MySQL
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute(
'SELECT * FROM accounts WHERE username = %s AND password = %s', (username, password,))
# Fetch one record and return result
account = cursor.fetchone()
# If account exists in accounts table in out database
if account:
# Create session data, we can access this data in other routes
session['loggedin'] = True
session['id'] = account['id']
session['username'] = account['username']
# Redirect to home page
return redirect(url_for('client.home'))
else:
# Account doesnt exist or username/password incorrect
msg = 'Incorrect username/password!'
# Show the login form with message (if any)
return render_template('index.html', msg=msg)
It redirects to this Blueprint:
from flask import Blueprint, render_template
from flask import render_template, request, redirect, url_for, session, flash
from flask_mysqldb import MySQL
import MySQLdb.cursors
import re
from extension import mysql
client = Blueprint('client', __name__,
static_folder="../static", template_folder="../templates")
#client.route('/')
def home():
if 'loggedin' in session:
cur = mysql.connection.cursor()
cur.execute('SELECT * FROM cliente')
data = cur.fetchall()
# User is loggedin show them the home page
return render_template('home.html', username=session['username'], cliente=data)
# User is not loggedin redirect to login page
return redirect(url_for('login'))
It works just fine, but with a condition. On my main.py I also have this:
#app.route('/home')
def home():
pass
And this is the problem, I don't know why I should keep this route on my main.py because if I delete it my app crashes and throws me this error:
werkzeug.routing.BuildError
werkzeug.routing.BuildError: Could not build url for endpoint 'home'. Did you mean 'client.home' instead?
I have no idea why does this happens.
Why should I keep this route? or What I'm doing wrong?
Could you please give me a hand?
I've trying to change the redirect to using multiple routes, but if I delete that /home route.. my app crashes anyway.
in url_for look for the name of the function ie url_for('function_name', parameters)
so to avoid the crash better to change the name of main.py home function to something else.
Solved: I had one ref to Home on other file: Layout.html.
Just removed the ref and it's solved

Why does Flask server avoids specific port opening?

When I specify an IP and port without the preceding:
if __name__ == '__main__':
As such:
app.run(host="138.165.91.210",port=5017,threaded=True)
It works but it hangs a bit (does it hang on an infinite loop maybe?). When I do the usual:
if __name__ == '__main__':
app.run(host="138.165.91.210",port=5017,threaded=True)
It avoids the ip and port. What's wrong with my code?
Full script:
import os
from sqlite3 import dbapi2 as sqlite3
from flask import Flask, request, session, g, redirect, url_for, abort, \
render_template, flash
# create our little application :)
app = Flask(__name__)
# Load default config and override config from an environment variable
def connect_db():
"""Connects to the specific database."""
rv = sqlite3.connect(app.config['DATABASE'])
rv.row_factory = sqlite3.Row
return rv
def init_db():
"""Initializes the database."""
db = get_db()
with app.open_resource('schema.sql', mode='r') as f:
db.cursor().executescript(f.read())
db.commit()
#app.cli.command('initdb')
def initdb_command():
"""Creates the database tables."""
init_db()
print('Initialized the database.')
def get_db():
"""Opens a new database connection if there is none yet for the
current application context.
"""
if not hasattr(g, 'sqlite_db'):
g.sqlite_db = connect_db()
return g.sqlite_db
#app.teardown_appcontext
def close_db(error):
"""Closes the database again at the end of the request."""
if hasattr(g, 'sqlite_db'):
g.sqlite_db.close()
#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'))
#app.route("/hello")
def hello():
return "Hello World!"
if __name__ == '__main__':
app.run(host="138.165.91.210",port=5017,threaded=True)
Edit:
I'm running the app using bash:
flask --app=flaskr run
You are using an unreleased version of Flask, so keep in mind that things might change.
__name__ == '__main__' only evaluates to True when you execute a file directly (i.e., python filename.py). Since that isn't how you're running it here, it will be False and that block is skipped.
To solve your port issue, when running your application using the flask command, you need to specify your options through the command line.
python -m flask --app flaskr --host 138.165.91.210 --port 5017 --with-threads
For more information, you should check the usage
python -m flask --help
To solve your delay issue, the if __name__ == '__main__': block is important. Without it, Flask will try to run two instances of the application (once from the flask command and one from the call to app.run).

Flask-login with static user always yielding 401- Unauthorized

I am trying to build a super simple web app for my own use. I'll be the only user, so I don't feel the need to involve a database for user management. I'm trying to use flask-login, but even though my call to login_user succeeds, I'm still met with a 401-Unauthorized page after the redirect to a page with #login_required. Here is the entirety of my app:
from flask import Flask, render_template, request, flash, redirect, url_for
from flask.ext.login import LoginManager, login_user, logout_user, current_user, login_required, UserMixin
app = Flask(__name__, static_url_path="")
app.secret_key = "[redacted]"
login_manager = LoginManager()
login_manager.init_app(app)
#login_manager.login_view = "login"
class User(UserMixin):
def __init__(self, id):
self.id = id
nathan = User('nathan')
#login_manager.user_loader
def load_user(userid):
if userid == 'nathan':
return nathan
else:
return None
#app.route("/logout")
#login_required
def logout():
logout_user()
return redirect(url_for('login'))
#app.route('/')
#login_required
def index():
return render_template('index.html')
#app.route("/login", methods=["GET", "POST"])
def login():
if request.method == 'POST':
if request.form['username'] == 'nathan'\
and request.form['password'] == '[redacted]':
login_user(nathan, remember=True)
flash('logged in...', 'success')
return redirect(request.args.get("next") or url_for("index"))
else:
flash('Incorrect username or password. Try again.', 'error')
return render_template("login.html");
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
I've verified that the login actually succeeds (login_user returns true) and that load_user is returning the nathan object.
[EDIT] Also, I'm currently using the Flask development server, in case that's relevant.
Not sure where I'm going wrong. Thanks in advance!
Update:
Since a newer version(0.2.2) of Flask-Login this is no more an issue. Check out the changes in this commit.
If you are using an older version, read on.
The problem here is static_url_path="". For Flask-Login to work you can not have an empty string static_url_path.
The following lines in the Flask-Login source(older version) reveal this:
if (current_app.static_url_path is not None and
request.path.startswith(current_app.static_url_path)
):
# load up an anonymous user for static pages
_request_ctx_stack.top.user = self.anonymous_user()
return
Since your static_url_path is "" the if condition evaluates to True, because of which every page you visit acts like a static page, and hence Flask-Login always loads an anonymous user, instead of continuing to load the actual user(using the load_user callback).
Also do not forget to uncomment #login_manager.login_view = "login"
If you still want to use the root folder of the app itself as the static folder, take a look at this solution, using SharedDataMiddleWare:
app.debug = True
if app.config['DEBUG']:
from werkzeug import SharedDataMiddleware
import os
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
'/': os.path.dirname(__file__)
})
if __name__ == "__main__":
app.run(host="0.0.0.0")

Browser caching issues in flask

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

Categories