My app runs completely fine on my computer (locally), however I'm confused why that is not the case once deployed to Heroku.
When the user registers, I can see all the infos in my Database, but when I try to login I have to try
2-3 times then it works.
Example:
I enter my username/password, click login. (Doesn't work)... Then I repeat the same step and it somehow works.
Also, when I'm logged in, and click somewhere to go to another route, I get disconnected/logged out.
I'm thinking maybe somehow the app is having issue getting the id of the logged in user.
Any tips would be really appreciated!
What my application.py looks like
import sys
import os
import re
from datetime import datetime
from flask import Flask, flash, redirect, render_template, request, session, jsonify
from flask_session import Session
from tempfile import mkdtemp
from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
from werkzeug.security import check_password_hash, generate_password_hash
import json
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import create_engine, and_, desc
from sqlalchemy.orm import scoped_session, sessionmaker
from decimal import Decimal
import urllib.request
import urllib
# Configure application
app = Flask(__name__)
ENV = ''
if ENV == 'dev':
app.debug = True # If in Devlopment Mode = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:postgres#localhost/finance'
else:
app.debug = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://databaseurl...'
# Ensure templates are auto-reloaded
app.config["TEMPLATES_AUTO_RELOAD"] = True
# Ensure responses aren't cached
#app.after_request
def after_request(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
# Custom filter
app.jinja_env.filters["usd"] = usd
# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_FILE_DIR"] = mkdtemp()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
#________________ DATABASE models __________________________
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True, nullable=False)
username = db.Column(db.String(30), unique=True, nullable=False)
hash = db.Column(db.String, nullable=False)
email = db.Column(db.String(100), unique=True, nullable=False)
cash = db.Column(db.Numeric, default=10000, nullable=False)
country = db.Column(db.String, nullable=False)
def __init__(self, username, hash, email, cash, country):
self.username = username
self.hash = hash
self.email = email
self.cash = cash
self.country = country
This is my /login route for example;
#app.route("/login", methods=["GET", "POST"])
def login():
"""Log user in"""
# Forget any user_id
session.clear()
# User reached route via POST (as by submitting a form via POST)
if request.method == "POST":
# Ensure username was submitted
if not request.form.get("username"):
return message("must provide username", 403)
# Ensure password was submitted
elif not request.form.get("password"):
return message("must provide password", 403)
# Get the input from username field
username = request.form.get("username")
# Query database for that username
rows = User.query.filter(User.username==username).all()
# Ensure username exists and password is correct
if len(rows) != 1 or not check_password_hash(rows[0].hash, request.form.get("password")):
return message("invalid username and/or password", 403)
# Remember which user has logged in
session["user_id"] = rows[0].id
# Redirect user to home page
return redirect("/")
Related
I’m coding a web app using flask, I’ve connected to an external database service using the correct syntax (so the URI in the code is removed for obvious reasons additionally the secret key will not be "replace-later" in final product). I want to know if there is any way to pinpoint what is causing the key error? The libraries I’m using for the web app are flask, flask-wtf, flask-sqlalchemy; additionally as I want to connect to a Postgres’s db I have downloaded Postgres utilities from the Postgres website and I have a DB host service available too . What could be the solution to this error?
Here is the main app code, this contains app logic and whatnot:
from models import *
app = Flask(__name__)
app.secret_key = 'replace-later'
app.config['SQLALCHEMY_DATABASE_URI']='**REMOVED**'
db = SQLAlchemy(app)
#app.route("/", methods=['GET','POST'])
def index():
reg_form = RegistrationForm()
if reg_form.validate_on_submit():
username = reg_form.username.data
password = reg_form.password.data
user_object = User.query.filter_by(username=username).first()
if user_object:
return "Username has been taken"
user = User(username=username, password=password)
db.session.add(user)
db.session.commit()
return "Added to DB"
Here is the link to the the db model which interacts with the database I have set up as well as the main app file in order to add user's usernames and passwords:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
""" User model """
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(25), unique=True, nullable=False)
password = db.Column(db.String(), nullable=False)
here is an image of the traceback and the error: image
I have resolved this issue by removing the duplicate "db" variable from both files and moving the database table model (class User) to the main python file (first one shown).
This however presents a new problem now I am trying to add a custom validator to another file I have for form logic this however with the previous changes gives me basically the same error. Here is the code I've ended up on.
There are 3 files:
This is the main web-app logic file containing routes and etc:
from wtform_fields import *
from libs import Flask, render_template
app = Flask(__name__)
app.secret_key = 'replace-later'
app.config['SQLALCHEMY_DATABASE_URI']='**REMOVED**'
# database model
db = SQLAlchemy(app)
class User(db.Model):
""" User model """
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(25), unique=True, nullable=False)
password = db.Column(db.String(), nullable=False)
#app.route("/", methods=['GET','POST'])
def index():
reg_form = RegistrationForm()
if reg_form.validate_on_submit():
username = reg_form.username.data
password = reg_form.password.data
user = User(username=username, password=password)
db.session.add(user)
db.session.commit()
return "Added to DB"
return render_template("index.html", form=reg_form)
if __name__ == "__main__":
app.run(debug=True)
This is the libs file which holds all the imported libraries - This was created as i thought this might be causing the error
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import InputRequired, Length, EqualTo, ValidationError
This is the form logic file:
from libs import SQLAlchemy, FlaskForm, StringField, PasswordField, SubmitField, InputRequired, Length, EqualTo, ValidationError
from application import User
class RegistrationForm(FlaskForm):
'''Registration Form'''
username = StringField('username_label',
validators=[InputRequired(message="Username Required"),
Length(min=4, max=25, message="Username must be between 4-20 charecters.")]
)
password = PasswordField('password_label',
validators=[InputRequired(message="Password Required"),
Length(min=8, max=25, message="Password must be between 8-25 charecters.")]
)
conf_pswd = PasswordField('conf_pswd_label',
validators=[InputRequired(message="Retype Password"),
EqualTo('password', message="Passwords must match")]
)
submit_button = SubmitField('create')
def validate_username(self, username):
user_object = User.query.filter_by(username=username.data).first()
if user_object:
raise ValidationError("Username already exists")
This is the traceback for the error in the edited files: Traceback
My question is how can I firstly roll back the code back to the first instance (I'm using git so I am sure this is in some way possible) then resolve the key error in the first instance so that the DB model is in a separate file also that there is no libs file; Additionally if how would I be able to adapt the code in the second instance to match the first (This is optional as I have a general idea on how to do it but help would be appreciated). Also are there any suggestions as to how I could tidy up my code so it's more understandable and formatted.
The main question is how do I fix the KeyError (In the first instance)?
I've tried just about everything - checking for memory leaks checking for duplicate keys and variables and stuff like that. When the issue is resolved I want the program to be able to add users and their passwords to the Database as well as allowing the fields to be checked using a custom validator - and other validators I may add.
I'm trying to create a registration page where the user's username and password would get sent to a database I have set up on Heroku. I do not get any errors and after clicking the submit button I get sent to the "you are registered" page, but the username and password don't get added to the db. Here is the code:
from flask import Flask, session, render_template, request
from flask_session import Session
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from models import *
app = Flask(__name__)
# Check for environment variable
if not os.getenv("DATABASE_URL"):
raise RuntimeError("DATABASE_URL is not set")
# Configure session to use filesystem
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
app.config['SQLALCHEMY_ECHO'] = True
Session(app)
# Set up database
engine = create_engine(os.getenv("DATABASE_URL"))
db = scoped_session(sessionmaker(bind=engine))
db1 = SQLAlchemy(app)
class User(db1.Model):
__tablename__ = "users"
user_id = db1.Column(db1.Integer, primary_key=True,)
username = db1.Column(db1.String, nullable=False)
password = db1.Column(db1.String, nullable=False)
def add_user(self, u):
self.users.append(u)
u.user_id = self.id
with app.app_context():
db1.init_app(app)
db1.create_all()
#app.route("/", methods=['POST', 'GET'])
def main():
return render_template('main.html')
#app.route("/registered", methods=['POST'])
def registered():
username = request.form.get('rusername')
password = request.form.get('rpassword')
u1 = User(username=username, password=password)
db1.session.add(u1)
db1.session.commit()
return '<h1> You are registered! <h1>'
#app.route("/loggedin", methods=['POST'])
def loggedin():
return '<h1> You are logged in! <h1>'
Thank you for your help!
I have created a login page with wraps & functools. I have done this previously and it worked, but this time I have stored the username & password in a PostgreSQL database and queried it using the sqlalchemy flask extension.
If the correct details have been entered, it logs you into the admin page, but if you try to type on the admin page, it doesn't redirect you back to the login page.
from flask import Flask, render_template, url_for, request, redirect, flash, session
from functools import wraps
app = Flask(__name__)
app.secret_key = "pythonAkoto"
def login_required(f):
#wraps(f)
def wrap(*args, **kwargs):
if 'logged_in' in session:
return f(*args, **kwargs)
else:
flash('You need to sign in first')
return redirect(url_for('login'))
return wrap
from sqlalchemy.orm import sessionmaker, relationship
# # this part is needed to create session to query database. this should be JUST BELOW app.config..
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey, select
meta = MetaData()
engine = create_engine("postgresql://postgres:password#localhost/db_name", echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
# posts database
class PostTable(Base):
__tablename__ = 'poststable'
id = Column('id', Integer, primary_key=True)
user = Column('username', String(20))
posts = Column('posts', String(30))
def __init__(self, user, posts):
self.user = user
self.posts = posts
# login database
class UserLogin(Base):
__tablename__ = 'user_login'
id = Column('id', Integer, primary_key=True)
username = Column('username', String(30))
userPW = Column('user_pw', String(20))
def __init__(self, username, user_pw):
self.username = username
self.user_pw = user_pw
Session = sessionmaker(bind=engine)
db_session = Session() # i have called this db_session to not get mixed up with sessions when logging in.
# write posts on wall
#app.route('/', methods=['GET', 'POST'])
def postpage():
title = "Posts"
name = request.form.get('name')
posts = request.form.get('posts')
if request.method == 'GET':
data = db_session.query(PostsTable).all()
flash('Write something on the wall!')
return render_template("postpage.html", title=title, data=data)
else:
db_entry = PostsTable(name, posts)
db_session.add(db_entry)
db_session.commit()
data = db_session.query(PostsTable).all()
flash(f'{name} just wrote a post!')
return render_template("postpage.html", data=data, title=title)
# login into admin page
#app.route('/login', methods=['GET', 'POST'])
def login():
title = "Login"
user = request.form.get("username")
upw = request.form.get("password")
if request.method == 'GET':
return render_template("login.html", title=title)
else:
db_user = db_session.query(UserLogin).filter(UserLogin.username==f'{user}')
db_pw = db_session.query(UserLogin).filter(UserLogin.userPW==f'{upw}')
result = db_user.first() and db_pw.first()
if result:
session['logged_in'] = True
flash(f'Hey {user}, you have just logged in!')
return redirect(url_for('admin'))
else:
error = "Invalid credentials. Please enter a valid username and/or password."
return render_template('login.html', error=error, title=title)
# admin page
#app.route('/admin')
#login_required
def admin():
title = "Admin"
return render_template("admin.html", title=title)
if __name__ == '__main__':
app.run(debug=True)
When you enter admin url, it's supposed to redirect you to the login page, but this doesn't happen; instead it takes you to the admin page without logging in!
I've done this before (without the database) and it worked fine, but this way isn't working and I can't seem to understand why.
I am using Flask-Security to set up user authentication for my app but I am struggling with getting the email address or ID of the currently logged in user so I can query a table with that particular users details. I've just using the standard code.
Another question here suggested the following but it didn't work:
my_user = current_user.get_id()
Standard code:
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_security import Security, SQLAlchemyUserDatastore, \
UserMixin, RoleMixin, login_required
# Create app
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'super-secret'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
# Create database connection object
db = SQLAlchemy(app)
# Define models
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))
class Role(db.Model, RoleMixin):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='dynamic'))
# Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
# Create a user to test with
#app.before_first_request
def create_user():
db.create_all()
user_datastore.create_user(email='matt#nobien.net', password='password')
db.session.commit()
# Views
#app.route('/')
#login_required
def home():
return render_template('index.html')
if __name__ == '__main__':
app.run()
You can use flask_login.current_user object. Its class will be what you configured Flask-Security to use to handle your users management, e.g. User for code you included.
Michael was half right, the issue is that the standard code example does not import sessions and whilst flask-security does in the back end set the session, it's not available in your flask app. This code from Michael:
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
session['email'] = request.form['email']
Whilst correct in a standard app would likely break with flask-security or at least be unnecessary as flask-security completely takes control of the /login route and handles the form securely. The only two changes needed to the flask example app are:
Changing the flask import line to:
from flask import Flask, render_template, session
This is an example of getting the user id from the session:
#app.route('/dashboard')
#login_required
def dashboard():
user_id = session["user_id"]
return name
Hope this helps someone as it took me a while to get my head around..
IMHO you can implement sessions from Flask.
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
#app.route('/')
def index():
if 'username' in session:
print("Currents user's ID is %s" % session['id']
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
session['email'] = request.form['email']
session['id'] = request.form['id']
return redirect(url_for('index'))
return '''
<form method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
#app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
session.pop('email', None)
session.pop('id', None)
return redirect(url_for('index'))
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
See: http://flask.pocoo.org/docs/0.12/quickstart/#sessions
In flask 2.0.x user id is automaticly saved in session under _user_id, not sure how or where this changed, in the official flask docs it still uses g.user, but maybe its outdated? I couldnt get it to work for me.
The simplest way i could get it to check if a user was login in with if '_user_id' in sessions.keys(), the return value of session['_user_id'] is a string of User.id
Its better to use session for this. You can store the information is session then use it anywhere.
In your login function just store the value like:
first import the session from flask.Then use like this.
session['username'] = login_form.username.data
then use it like {{ session['username'] }} in your template.
I am Creating a Flask Application.Application is running perfectly on local server. There is no error but the data is not stored in the database. What am i Missing?
1.routes.py
from flask import Flask,render_template,request
from models import db ,User
from forms import SignupForm
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:////Database.db"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db. init_app(app)
app.secret_key ='development_key'
#app.route('/' , methods=['GET','POST'])
def index():
form = SignupForm() #form object
if request.method == 'POST':
if form.validate() == False:
return render_template('index.htm' , form=form)
else:
#new user to be added to database newuser = User(form.fname.data,form.lname.data,form.email.data,form.password.data)
db.create_all()
db.session.add(newuser)
db.session.commit()
return render_template('profile.htm')
elif request.method == 'GET':
return render_template('index.htm',form=form)
#app.route('/aboutus')
def aboutus():
return render_template('aboutus.htm')
#app.route('/profile')
def profile():
return render_template('profile.htm')
#app.route('/contactus')
def contactus():
return render_template('contactus.htm')
2.app.py
from routes import app
app.run(debug = True)
3.forms.py
from flask_wtf import FlaskForm as Form
from wtforms import StringField,PasswordField,SubmitField
from wtforms.validators import DataRequired,Email,Length
class SignupForm(Form):
fname = StringField('First Name',validators=[DataRequired("Please Enter Your FirstName")])
lname = StringField('Last Name',validators=[DataRequired("Please Enter Your LastName")])
password = PasswordField('Password',validators=[DataRequired("Password Can't Be Empty"), Length(min=8,message="Password Must Be 8 character Long")])
email = StringField('Email',validators=[DataRequired("Please Enter Your Email") , Email("Please Enter A Valid Email")])
submit = SubmitField('Register')
4.models.py
from flask_sqlalchemy import SQLAlchemy
from werkzeug import generate_password_hash, check_password_hash
db = SQLAlchemy()
class User(db.Model):
__tablename__= "users"
id=db.Column(db.Integer,primary_key=True)
fname = db.Column(db.String(100))
lname = db.Column(db.String(100))
email = db.Column(db.String(100),unique=True)
password = db.Column(db.String(100))
def __init__(self,fname,lname,email,password):
self.fname=fname
self.lname=lname
self.email=email
self.set_password(password)
def set_password(self,password):
self.password=generate_password_hash(password)
def check_password(self,password):
return check_password_hash(self.password,password)
I tried your code. The database wasn't being created. Try something like this instead to ensure your database file is in the proper place:
import os
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'Database.db')
Do not use sqlite3 for production purpose and rather use postgresql because sqlite3 is good for local machine stuff and does not work well on the online servers as it is also does not works well on the heroku hosting platform where the database gets reset at least once in every 24 hours for which you can even check the following link https://devcenter.heroku.com/articles/sqlite3 though you did not mentioned using heroku but I am still suggesting this because heroku is very common hosting place.