I recently tried deploying my Flask application to Heroku and have been running into a few issues. When testing the application locally I have no issues w/ the app at all. Everything like the user registration/login works perfectly and item form submission works perfectly.
However when I visit the app at https://simplify-exp.herokuapp.com I get weird issues when doing any task related to form submission (user login/registration and item creation). When something is submitted the webpage seems to do nothing but the third or fourth time I hit the submit button it works fine. Form submission seems to only work half the time and I have no idea why. There is just erratic behavior when any for is submitted regarding redirecting.
Here is an example of the login not working properly. It works perfectly locally.
Here is an example of logging out going back to the login page when it should go to the main page w/ the sign up and login buttons (the one w/ the blue background).
If you visit the site and use it for a few minutes then you will see the issues.
Included below is part of the code and the heroku logs. Any help would be appreciated.
I tried redeploying the app and restarted the db connection as I thought that was my initial mistake. Tried googling the error but found nothing.
import os
import datetime
from dateutil.parser import parse
from collections import defaultdict, OrderedDict
from flask import Flask, render_template, redirect, url_for, request
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField
from wtforms.validators import InputRequired, Email, Length
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_migrate import Migrate
from flask_heroku import Heroku
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(32)
# app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://localhost/simplify'
bootstrap = Bootstrap(app)
heroku = Heroku(app)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(15), unique=True)
password = db.Column(db.String(100))
items = db.relationship('Item', backref='user', lazy='dynamic')
def __repr__(self):
return "ID: {}, Username: {}, Items: {}".format(self.id,self.username,self.items)
class Item(db.Model):
id = db.Column(db.Integer, unique=True, nullable=False, primary_key=True, autoincrement=True)
date = db.Column(db.DateTime)
name = db.Column(db.String(80))
price = db.Column(db.Float)
category = db.Column(db.String(80))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return "ID: {}, Date: {}, Name: {}, Price: {}, Category: {}, User_id: {}".format(self.id,self.date,self.name,self.price,self.category,self.user_id)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class LoginForm(FlaskForm):
username = StringField('username', validators=[InputRequired(), Length(min=1, max=15)])
password = PasswordField('password', validators=[InputRequired(), Length(min=1, max=80)])
remember = BooleanField('remember me')
class RegisterForm(FlaskForm):
username = StringField('username', validators=[InputRequired(), Length(min=1, max=15)])
password = PasswordField('password', validators=[InputRequired(), Length(min=1, max=80)])
@app.route('/')
def index():
return render_template('index.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user:
if check_password_hash(user.password, form.password.data):
login_user(user, remember=form.remember.data)
return redirect(url_for('dashboard'))
return '<h1>Invalid username or password</h1>'
return render_template('login.html', form=form)
@app.route('/signup', methods=['GET', 'POST'])
def signup():
form = RegisterForm()
if form.validate_on_submit():
hashed_password = generate_password_hash(form.password.data)
new_user = User(username=form.username.data, password=hashed_password)
db.session.add(new_user)
db.session.commit()
login_user(new_user)
return redirect(url_for('dashboard'))
return render_template('signup.html', form=form)
@app.route('/dashboard', methods=['GET', 'POST'])
@login_required
def dashboard():
add_item()
items = query_and_format_items()
categories,c_spending = get_category_spending(items)
months, m_spending = get_monthly_spending(items)
past_month_total_spending, past_month_categories, past_month_spending = get_past_30_days_spending(items)
return render_template('dashboard.html', items=items[:10], categories=categories, c_spending=c_spending, months=months, m_spending=m_spending, past_month_total_spending=past_month_total_spending, past_month_categories=past_month_categories, past_month_spending=past_month_spending)
@app.route('/history/', endpoint='history', methods=['GET', 'POST'])
@login_required
def history():
add_item()
items = query_and_format_items()
return render_template('history.html', items=items)
@app.route('/delete/<int:id>', methods=['POST'])
def remove(id):
item = current_user.items.filter(Item.id == id)
item.delete()
db.session.commit()
return redirect(url_for('history'))
# Adds item into db
def add_item():
if request.form:
item = Item(date=parse(request.form.get('date')), name=request.form.get('item_name'),price=request.form.get('price'),category=request.form.get('category').capitalize(),user_id=current_user.username,user=current_user)
db.session.add(item)
db.session.commit()
# Formats the date of all items
def query_and_format_items():
items = current_user.items.all()[::-1]
items.sort(key=lambda x: x.date, reverse=True)
for item in items:
item.date = item.date.strftime('%m-%d-%y')
return items
def get_category_spending(items):
category_amount = defaultdict(int)
for item in items:
category_amount[item.category] += item.price
return list(category_amount.keys()), list(category_amount.values())
def get_monthly_spending(items):
monthly_amount = OrderedDict()
for item in reversed(items):
item_date = get_month_year(item)
if item_date in monthly_amount:
monthly_amount[item_date] += item.price
else:
monthly_amount[item_date] = item.price
return list(monthly_amount.keys()), list(monthly_amount.values())
def get_month_year(item):
return item.date[:2] + '-' + item.date[6:]
def get_past_30_days_spending(items):
total_spending = 0
category_amount = defaultdict(int)
for item in items:
date_range = datetime.datetime.now() - parse(item.date)
if date_range.days <= 30:
category_amount[item.category] += item.price
total_spending += item.price
return total_spending, list(category_amount.keys()), list(category_amount.values())
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
These are the heroku logs when I try and submit a form either during user login/registration or during item creation.
019-06-23T20:48:34.666717+00:00 app[web.1]: 10.5.168.154 - - [23/Jun/2019:20:48:34 +0000] "POST /signup HTTP/1.1" 302 227 "https://simplify-exp.herokuapp.com/signup" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"
2019-06-23T20:48:34.666688+00:00 heroku[router]: at=info method=POST path="/signup" host=simplify-exp.herokuapp.com request_id=7f74da1c-4dc4-4034-9126-f59781f0258c fwd="96.255.28.103" dyno=web.1 connect=0ms service=301ms status=302 bytes=760 protocol=https
2019-06-23T20:48:34.833489+00:00 heroku[router]: at=info method=GET path="/dashboard" host=simplify-exp.herokuapp.com request_id=43955edb-f83c-4982-a2da-af9cea9a071c fwd="96.255.28.103" dyno=web.1 connect=0ms service=41ms status=200 bytes=7610 protocol=https
2019-06-23T20:48:34.833497+00:00 app[web.1]: 10.5.168.154 - - [23/Jun/2019:20:48:34 +0000] "GET /dashboard HTTP/1.1" 200 7434 "https://simplify-exp.herokuapp.com/signup" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"
2019-06-23T20:48:34.889270+00:00 app[web.1]: 10.5.168.154 - - [23/Jun/2019:20:48:34 +0000] "GET /static/dashboard.css HTTP/1.1" 200 0 "https://simplify-exp.herokuapp.com/dashboard" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"
2019-06-23T20:48:34.941411+00:00 app[web.1]: 10.5.168.154 - - [23/Jun/2019:20:48:34 +0000] "GET /resources/demos/style.css HTTP/1.1" 404 232 "https://simplify-exp.herokuapp.com/dashboard" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"
2019-06-23T20:48:34.889105+00:00 heroku[router]: at=info method=GET path="/static/dashboard.css" host=simplify-exp.herokuapp.com request_id=f5c6337e-6785-480a-968a-f6b4c1680ffd fwd="96.255.28.103" dyno=web.1 connect=1ms service=3ms status=200 bytes=2282 protocol=https
2019-06-23T20:48:34.940746+00:00 heroku[router]: at=info method=GET path="/resources/demos/style.css" host=simplify-exp.herokuapp.com request_id=7d0454ca-d38d-4b44-8de9-152c14496ed5 fwd="96.255.28.103" dyno=web.1 connect=1ms service=28ms status=404 bytes=385 protocol=https
2019-06-23T20:48:36.137821+00:00 heroku[router]: at=info method=GET path="/favicon.ico" host=simplify-exp.herokuapp.com request_id=5c917639-4275-4269-b254-4d64e47a3eda fwd="96.255.28.103" dyno=web.1 connect=0ms service=13ms status=404 bytes=385 protocol=https
2019-06-23T20:48:36.138430+00:00 app[web.1]: 10.5.168.154 - - [23/Jun/2019:20:48:36 +0000] "GET /favicon.ico HTTP/1.1" 404 232 "https://simplify-exp.herokuapp.com/dashboard" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"