I'm new to Python and Flask.
I'm following this tutorial http://douglasstarnes.com/index.php/2015/05/27/easy-authentication-with-flask-login/ to have registration and login pages and have slightly modified it to hash the passwords on registration and to verify the password against the hash on login.
Initial password registration hashing works, but verifying the hashed password stored in the database against the one given in plain text via a login form does not.
The error I am receiving is against the following line on /login page and for the following reason:
if user.count() == 1 and check_password_hash(user.password, password) == True:
AttributeError: 'BaseQuery' object has no attribute 'password'
I cannot work out why I'm receiving this error. The user gets successfully queried from the database, and the user has it's hashed password within the password column.
The query I'm using and method of returning data from the password column are similar to that included in the documentation http://flask-sqlalchemy.pocoo.org/2.1/queries/#querying-records
This is my views.py (/login and the erroring line are towards the bottom)
from flask import Flask, render_template, request, abort, redirect, url_for, flash
from flask.ext.login import LoginManager, UserMixin, login_user, logout_user, login_required
from flask.ext.sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from app import app
import os
login_manager = LoginManager(app)
login_manager.init_app(app)
login_manager.login_view = 'login'
login_manager.session_protection = "strong"
db = SQLAlchemy(app)
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String)
password = db.Column(db.String)
@login_manager.user_loader
def user_loader(user_id):
user = User.query.filter_by(id=user_id)
if user.count() == 1:
return user.one()
return None
@app.before_first_request
def init_request():
db.create_all()
@app.route('/secret')
@login_required
def secret():
return render_template('secret.html')
@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('index'))
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
return render_template('register.html')
elif request.method == 'POST':
username = request.form['txtUsername']
password = request.form['txtPassword']
user = User.query.filter_by(username=username)
if user.count() == 0:
hashed_password = generate_password_hash(password)
user = User(username=username, password=hashed_password)
db.session.add(user)
db.session.commit()
flash('You have registered the username {0}. Please login'.format(username))
return redirect(url_for('login'))
else:
flash('The username {0} is already in use. Please try a new username.'.format(username))
return redirect(url_for('register'))
else:
abort(405)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html', next=request.args.get('next'))
elif request.method == 'POST':
username = request.form['txtUsername']
password = request.form['txtPassword']
user = User.query.filter_by(username=username)
if user.count() == 1 and check_password_hash(user.password, password) == True:
login_user(user.one(), remember=True)
flash('Welcome back {0}'.format(username))
try:
next = request.form['next']
return redirect(next)
except:
return redirect(url_for('index'))
else:
flash('Invalid login')
return redirect(url_for('login'))
else:
return abort(405)
@app.route('/')
def index():
return render_template('index.html')
Does anyone know why I cannot access the password column for the user, or in fact any column? I've tried all 3 within the database, ID, username and password.
To query database we use the query () method of the session object. The query () method returns a flask_sqlalchemy.BaseQuery object which is just an extension of the original sqlalchemy.orm.query.Query object. The flask_sqlalchemy.BaseQuery object represents the SELECT statement that will be used to query the database.
We imported Flask, SQLAlchemy to help our Python application communicate with a database, Bcrypt for password hashing, Migrate for database migrations, and several other methods from Flask-Login for session management.
For this purpose Flask-SQLAlchemy provides a query attribute on your Model class. When you access it you will get back a new query object over all records. You can then use methods like filter () to filter the records before you fire the select with all () or first (). If you want to go by primary key you can also use get ().
Now user references a real User object, as opposed to a stored query. you cannot access a User Object attribute (password) from a Query Object Show activity on this post.
@login_manager.user_loader
def user_loader(user_id):
user = User.query.filter_by(id=user_id).first()
if user:
return user
return None
Applying the .first()
executes the query, instead of storing the Query object
it will return only the first result.
.all()
returns all
edit:
or you could use user = User.query.get(user_id)
assuming user_id
is defined as PK
In your Login Function it should be
user = User.query.filter_by(username=username).first()
if user and check_password_hash(user.password, password) == True:
login_user(user)
Now user references a real User object, as opposed to a stored query. you cannot access a User Object attribute (password) from a Query Object
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With