How to get current user when implementing Python Flask-Security? - python

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.

Related

Getting logged out sometimes when switching routes - Heroku Flask_SQLAlchemy

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("/")

How does Flask-login session works

Forgive me for my lack of knowledge. Am a complete newbie to flask and web technology concept.
I am in the process to build the login part of an app. After searching, I found flask login to be an option to use. After going through SQLAlchemy,Flask-login homepage, some blogs,tutorials,and going through questions on stack-oflow, tried to build a basic login part-code given below. I used SQLAlchemy, and database is POSTGres. This is just a start involving the login through email-password and session handling will involve more functions later.
In the code, I authenticate the user-id and password, and then assign corresponding UUID(primary key in User DB) from the database as a session variable, in order to create a session. Am I right in doing so?. In some 'stack-of' answers, it is mentioned that session-id is to be randomly generated after user authentication, and stored in a separate sessions table in database. Got confused.
I am passing UUID which is my primary key, as an 'id' for 'get_id' method. Is is right??
I tried implementing this code. However in chrome developement console, I see sessions, which dissappear after i logout.
import flask
from flask import Flask,render_template,request,url_for,redirect,session
from flask_sqlalchemy import SQLAlchemy
from flask_login import current_user, UserMixin, LoginManager, login_required, login_user,
logout_user
app = Flask(__name__)
db = SQLAlchemy(app)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:test#localhost/hw'
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
app.config['SECRET_KEY'] = 'thisissecret'
class User(UserMixin,db.Model):
__tablename__ = 'user'
__table_args__ = {'schema': 'logging'}
user_id = db.Column(db.VARCHAR,primary_key = True)
first_name = db.Column(db.VARCHAR)
last_name = db.Column(db.VARCHAR)
email = db.Column(db.VARCHAR)
contact_no = db.Column(db.VARCHAR)
status = db.Column(db.BOOLEAN)
loginpassword = db.Column(db.VARCHAR)
def get_id(self):
return str(self.user_id)
#login_manager.user_loader
def load_user(id):
try:
return User.query.get(id)
except:
return None
#app.route('/logedin',methods=['POST'])
def logedin():
session.pop('id', None)
em = request.form['email']
pwd = request.form['password']
usr = User.query.filter_by(email = em).first()
if usr:
if usr and usr.loginpassword == pwd:
login_user(usr,remember = False)
session['id'] = usr.user_id
return ('you r logged in')
else:
return '<h1>user not found</h1>'
else:
#return render_template('testlogin.html')
return '<h1>user not found</h1>'
#app.before_request
def check_logedin():
if not 'id' in session:
return render_template('testlogin.html')
#app.route('/login')
def login():
if current_user is_authenticated():
return redirect(url_for('home'))
else:
return render_template('testlogin.html')
#app.route('/logout')
#login_required
def logout():
logout_user()
session.pop('id', None)
return 'you are logged out'
#app.route('/home')
#login_required
def home():
return ('The current user is in.')
if __name__ == '__main__':
app.run(debug=True)
Apologies if some silly things. But I am unable to make out this session thing. Appreciate your help. Thanks in advance
You say that you "assign corresponding [user id] as a session variable", but some say "that session-id is to be randomly generated after user authentication, and stored in a separate sessions table in database".
Those two things are not in conflict. A session ID is a value sent to the user. It identifies session data. What is important is that the session data is hidden from the user and that a valid session ID cannot be guessed by a user.
What you are doing with flask.session is fine. You are placing a variable into a session and flask is taking care of the rest (giving only a random session ID to the user).
All you need to do is save user id in the session:
session['id'] = usr.user_id
and later read user id from the session
user_id = session.get('id')
Note that the user_id read this way may be None, meaning the user is not logged in.
This does not keep session data in a database, at least by default, but that probably is not important in your case. A good reason to keep data in a data base would be for example if you have a distributed system in which several servers are serving the same website, so the user might log in in one server, but then access another server.

Data from a form is not getting added to the database with flask-sqlalchemy

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!

DB is updated but api is not showing any content in Flask

I am building one website using Flask. The app.py, html template and db(postgresql) data is shown below.On hitting the URL http://127.0.0.1:5000/ we can enter the username and email. http://127.0.0.1:5000/users will show the content which is updated in db here it is Mak mak#mk.com.
app.py
from flask import Flask, render_template,request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =
'postgres://postgres:123#localhost/sample'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80))
email = db.Column(db.String(100), unique = True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '<User %r>' %self.username
#app.route('/')
#def index():
def index():
return render_template('add_user.html')
#app.route('/post_user', methods=['POST'])
def post_user():
user = User(request.form['username'], request.form['email'])
db.session.add(user)
db.session.commit()
return redirect(url_for('index'))
#app.route('/users')
def get_users():
users = User.query.all()
return render_template('get_user.html', user = users)
#app.route('/homepage')
def display_homepage():
return 'Display homepage'
if __name__ =='__main__':
app.run()
get_user.html
{% for singleuser in users %}
<ul>
<li>singleuser.username</li>
</ul>
{% endfor %}
Sql Schema details
1 "Mak" "mak#mak.com"
in get_users (), there's a typo in the return statement
return render_template('get_user.html', users = users)

Flask Data is not stored to database

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.

Categories