I would like to change a users password in db using db.session.commit()
I am getting the appropriate flash for form validation. But on next login, the db change does not go through / I cannot login with the newly created password. The old password is the one that needs to be used on the next login.
from Portfolio import db, login_manager
from Portfolio import bcrypt
from flask_login import UserMixin
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
id = db.Column(db.Integer(), primary_key=True)
username = db.Column(db.String(length=30), nullable=False, unique=True)
password_hash = db.Column(db.String(length=60), nullable=False)
#property
def password(self):
return self.password
#password.setter
def password(self, plain_text_password):
self.password_hash = bcrypt.generate_password_hash(plain_text_password)
def check_password_correction(self, attempted_password):
return bcrypt.check_password_hash(self.password_hash, attempted_password)
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
app.config['SECRET_KEY'] = 'c133ce687016b5000d7b56cc81e0d974c9f1b0730836b4997765c34c7f417c56'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
login_manager.login_view = "login_page"
login_manager.login_message_category = "info"
from Portfolio import routes
class ResetForm(FlaskForm):
def validate_reset(self, reset_to_check):
password = User.query.filter_by(password_hash=reset_to_check.data).first()
if password:
raise ValidationError('Please input a proper password')
resetpass = PasswordField(label='Reset Password',
validators=[Regexp('^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!#$%^&*-]).{12,}$'),
Length(min=12), DataRequired()])
confreset = PasswordField(label='Confirm Changed Password:', validators=[EqualTo('resetpass'), DataRequired()])
newsubmit = SubmitField(label='Submit New Password')
#app.route('/reset', methods=['GET', 'POST'])
#login_required
def reset():
form = ResetForm()
if form.validate_on_submit():
user = User.username
reset_password = User(password=form.resetpass.data)
user.password = reset_password
db.session.commit()
logout_user()
flash('Password has been changed. Please login.')
return redirect(url_for('login_page'))
return render_template('reset.html', form=form, date=format_date, time=format_time)
Are you sure that validation step passed?
Here you compared the incoming data (probably not hashed) with hashed password in db.
password = User.query.filter_by(password_hash=reset_to_check.data).first()
if password:
raise ValidationError('Please input a proper password')
I assume that it will always be None, so no raise.
Second thing, you assign new User instance (why creating a new user?) to reset_password variable and afterwards assign this User instance under reset_password to user.reset_password atrribute. It is awkward and wrong for me. You should reset password for current_user :
from flask_login import current_user, logout_user
#app.route('/reset', methods=['GET', 'POST'])
#login_required
def reset():
form = ResetForm()
if form.validate_on_submit():
user = current_user
user.password = resetpass.data
db.session.commit()
logout_user()
flash('Password has been changed. Please login.')
return redirect(url_for('login_page'))
return render_template('reset.html', form=form, date=format_date, time=format_time)
Related
How can I create a default guest user with username guest and password equal to password when I start the web server; i.e. flask run?
The purpose of this default guest user is to be a demo user so that the actual user doesn't have to register and be able to test and tour the web app.
models.py
from flask_login import UserMixin
from app import bcrypt, db, login_manager
#login_manager.user_loader
def load_user(user_id: int):
return User.query.get(user_id)
class User(db.Model, UserMixin):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True, autoincrement=True, unique=True)
username = db.Column(db.String(60), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
def __init__(self, username="guest", password="password"):
self.username = username
self.password = bcrypt.generate_password_hash(password).decode("UTF-8")
def __repr__(self):
return f"<User {self.username!r}>"
#classmethod
def authenticate(cls, username, password):
user = cls.query.filter_by(username=username).first()
if user and bcrypt.check_password_hash(user.password, password):
return user
return False
views.py
from flask import Blueprint, flash, redirect, render_template, url_for
from flask_login import current_user, login_user
from app.auth.forms import SigninForm
from app.models import User
auth = Blueprint("auth", __name__, url_prefix="/auth")
#auth.route("/signin", methods=["GET", "POST"])
def signin():
if current_user.is_authenticated:
return redirect(url_for("main.home"))
form = SigninForm()
if form.validate_on_submit():
user = User.authenticate(
username=form.username.data, password=form.password.data
)
if user:
login_user(user, remember=form.remember.data)
flash(f"Hello, {form.username.data}", category="info")
return redirect(url_for("main.home"))
else:
flash("Login Unsuccessful. Please check username and password", "danger")
return render_template(
"auth/signin.html", title="Sign in", icon="log-in", form=form
)
EDIT:
My idea is to add the guest user manually to the DB even before the flask run. But what if the code is redistributed? I want to make the guest user creation to be automated using Python.
You could try the before_first_request decorator.
Docs: https://flask.palletsprojects.com/en/2.2.x/api/#flask.Flask.before_first_request
So something like this
from flask import Flask
app = Flask(__name__)
...
#app.before_first_request
def create_guest_user():
# include code to create new user here
pass
I keep receiving this error on when trying to launch my application and, despite looking through many Stack overflow posts that had my same error, nothing I tried seems to work.
I have the database existing in my current directory, the databse_uri seems to be correct, I have configured the application before creating the database (db) and I have created the database before connecting to it. What am I doing wrong?
import api_requests #to process the requests from users
#creating the user class
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm #for creating forms through flask
from flask_wtf.file import FileField, FileRequired, FileAllowed
from wtforms import StringField, PasswordField, SubmitField, RadioField #for creating fields in input forms
from wtforms.validators import InputRequired, Length, ValidationError #for validating user input in the forms
from flask_login import UserMixin
app = Flask(__name__)
#app configurations
app.config["SECRET_KEY"]= SECRET_KEY
app.config["MAX_CONTENT_LENGTH"] = 100*1024*1024 #100MB max-limit per image
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] =False
app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:///Users.db'
bcrypt = Bcrypt(app)
db= SQLAlchemy(app)
login_manager=LoginManager()
login_manager.init_app(app)#will allow flask and login manager to work together when users are logging in
login_manager.login_view ="login"
class Users(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(100), nullable=False, unique=True)
password = db.Column(db.String(100), nullable=False)
#creating the registration form
class RegisterForm(FlaskForm):
username = StringField(validators=[InputRequired(), Length(min=4, max=100)], render_kw={"placeholder":"Username"})
password = PasswordField(validators=[InputRequired(), Length(min=4, max=100)], render_kw={"placeholder": "password"})
confirm_password = PasswordField(validators=[InputRequired(), Length(min=4, max=100)], render_kw={"placeholder": "confirm_password"})
submit = SubmitField("Register")
def validate_username(self, username):
existing_user_username = Users.query.filter_by(username=username.data).first()
if existing_user_username:
raise ValidationError("That username already exists. Please pick another one.")
#creating the login form
class LoginForm(FlaskForm):
username = StringField(validators=[InputRequired(), Length(min=4, max=100)], render_kw={"placeholder":"Username"})
password = PasswordField(validators=[InputRequired(), Length(min=4, max=100)], render_kw={"placeholder": "password"})
submit = SubmitField("Login")
#creating the upload image form
class UploadImage(FlaskForm):
file = FileField(validators=[FileRequired(), FileAllowed(['png', 'jpeg','jpg'], 'Images only!')]) #allow only files with the correct extension to be submitted
organs = RadioField('Label', choices=[('leaf','leaf'),('flower','flower'),('fruit','fruit'),('bark','bark/stem')])
upload = SubmitField("Upload")
#login_manager.user_loader
def load_user(user_id):
return Users.get(user_id) # loads the user object from the user id stored in the session
#app.route("/new_user", methods=["GET", "POST"])
def register_user():
form = RegisterForm()
if request.method == "POST":
if form.validate_on_submit():
if form.confirm_password.data != form.password.data:
flash("the two password fields don/t match, please enter them correctly")
return render_template('new_user.html', form = form)
hashed_password = bcrypt.generate_password_hash(form.password.data)
new_user = Users(username=form.username.data, password= hashed_password)
db.session.add(new_user)
db.session.commit()
return redirect(url_for("login"))
#insert something here
flash("Username already exists, please pick another one")
return render_template("new_user.html", form=form)
#app.route("/log", methods=["GET", "POST"])
def login():
form = LoginForm()
if form.validate_on_submit():
#check if user is in db
user = Users.query.filter_by(username =form.username.data).first()
if user:
if bcrypt.check_password_hash(user.password,form.password.data):
login_user(user)
return redirect(url_for("view_plants"))
flash("Username or password entered incorrectly. Please try entering them again.")
return render_template("index.html", form=form)
here is my code:
Check your Flask-SQLAlchemy version- you might need to downgrade to 2.5.1.
I'm trying to make a login page I have been trying for weeks but this error keeps popping up:
sqlalchemy.orm.exc.FlushError: Instance <Users at 0x10bd8c580> has a NULL identity key.
The error lies in the register.py file. Apparently flask doesn't like me using .commit() or .add(). I've also tried to use .flush
but it gave me the same error still
register.py:
from flask import Blueprint, url_for, render_template, redirect, request
from flask_login import LoginManager
from werkzeug.security import generate_password_hash
import sqlalchemy
from models import db, Users
register = Blueprint('register', __name__, template_folder='../frontend')
login_manager = LoginManager()
login_manager.init_app(register)
#register.route('/register', methods=['GET', 'POST'])
def show():
if request.method == 'POST':
username = request.form['username']
email = request.form['email']
password = request.form['password']
confirm_password = request.form['confirm-password']
if username and email and password and confirm_password:
if password == confirm_password:
hashed_password = generate_password_hash(
password, method='sha256')
try:
new_user = Users(
username=username,
email=email,
password=hashed_password,
)
db.session.add(new_user)
db.session.commit()
except sqlalchemy.exc.IntegrityError:
return redirect(url_for('register.show') + '?error=user-or-email-exists')
return redirect(url_for('login.show') + '?success=account-created')
else:
return redirect(url_for('register.show') + '?error=missing-fields')
else:
return render_template('register.html')
Models.py:
from flask_login import UserMixin
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Users(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(15), unique=True)
email = db.Column(db.String(50), unique=True)
password = db.Column(db.String)
item = db.Column(db.String(25))
amount = db.Column(db.Integer)
I'm still a beginner with flask so excuse me if it's real obvious, any assistance would be very welcome!
You didn't specify what sql database you are using with sqlAlchemy. Anyways, the id is not being generated so the new_user has no identity. that's why your getting this error. To solve the problem modify your model as follows:
class Users(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(15), unique=True)
email = db.Column(db.String(50), unique=True)
password = db.Column(db.String)
item = db.Column(db.String(25))
amount = db.Column(db.Integer)
or you can use Sequence instead of autoincrement:
id = db.Column(db.Integer, primary_key=True, Sequence('user_seq'))
This is for the identity issue, but note that you need to specify user_loader for flask_login to work.
This question already has answers here:
How Can I Automatically Populate SQLAlchemy Database Fields? (Flask-SQLAlchemy)
(2 answers)
Closed 4 years ago.
I am new to SQLAlchemy and have been unable to set the DateTime for created. I have tried using the "default" option found in many examples. I have also tried setting it manually (I have it commented out). Neither have worked so far. Any help would be appreciated.
models.py
import datetime
from flask.ext.sqlalchemy import SQLAlchemy
from werkzeug import generate_password_hash, check_password_hash
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'users'
uid = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String(40))
lastname = db.Column(db.String(40))
email = db.Column(db.String(120), unique=True)
created = db.Column(db.DateTime, default=datetime.datetime.utcnow())
confirmed = db.Column(db.DateTime, nullable=True)
pwdhash = db.Column(db.String(100))
def __init__(self, firstname, lastname, email, password):
self.firstname = firstname.title()
self.lastname = lastname.title()
self.email = email.lower()
self.set_password(password)
#self.created = datetime.datetime.utcnow()
self.confirmed = None
def set_password(self, password):
self.pwdhash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.pwdhash, password)
routes.py
from tasks import app
from datetime import datetime
from flask import render_template, request, flash, session, url_for, redirect
from forms import ContactForm, SignupForm, SigninForm
from flask.ext.mail import Message, Mail
from models import db, User, Tags, Tasks
#app.route('/signup', methods=['GET', 'POST'])
def signup():
form = SignupForm()
if 'email' in session:
return redirect(url_for('profile'))
if request.method == 'POST':
if form.validate() == False:
return render_template('signup.html', form=form)
else:
newuser = User(form.firstname.data, form.lastname.data, form.email.data, form.password.data)
db.session.add(newuser)
db.session.commit()
session['email'] = newuser.email
return redirect(url_for('profile'))
elif request.method == 'GET':
return render_template('signup.html', form=form)
The problem with your default is that you're calling datetime.utcnow immediately there, and the value returned (at the time of class definition) is always used as default. You need to pass the callable itself like following:
# Note the lack of parenthesis after datetime.utcnow
created = db.Column(db.DateTime, default=datetime.datetime.utcnow)
This way SQLAlchemy will call datetime.utcnow itself upon row insert.
I got this error in my app flask
IntegrityError: (sqlite3.IntegrityError) NOT NULL constraint failed:
auth_user.role [SQL: u'INSERT INTO auth_user (username, email, password, role, status) VALUES (?, ?, ?, ?, ?)']
[parameters: (u'Natali', u'mail#gmail.com', 'pbkdf2:sha1:1000$p8jlOhqU$fa51e0491a729cef6d05dbd9f1d868455de4be9c', None, None)]
However, I think that the code is fine.
I don't know why doesn't work properly, the values that are as None are allowed to do it, because null=True.
My files are the following
This is my models.py
from app import db
from werkzeug import generate_password_hash, check_password_hash
class User(db.Model):
__tablename__ = 'auth_user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(128), nullable=False)
email = db.Column(db.String(128), nullable=False,
unique=True)
password = db.Column(db.String(192), nullable=False)
role = db.Column(db.SmallInteger, nullable=True)
status = db.Column(db.SmallInteger, nullable=True)
def __init__(self, username, email, password):
self.username = username.title()
self.email = email.lower()
self.password = generate_password_hash(password)
def __repr__(self):
return '<User %r>' % (self.name)
def set_password(self, password):
self.pwdhash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.pwdhash, password)
And this is my controller.py
from flask import (
Blueprint,
request,
render_template,
flash,
g,
session,
redirect,
url_for
)
from werkzeug import check_password_hash, generate_password_hash
from app import db
from app.authentication.forms import LoginForm, SignupForm
from app.authentication.models import User
mod_auth = Blueprint('auth', __name__, url_prefix='/auth')
#mod_auth.route('/profile')
def profile():
if 'email' not in session:
return redirect(url_for('signin'))
user = User.query.filter_by(email = session['email']).first()
if user is None:
return redirect(url_for('signin'))
else:
return render_template('authentication/profile.html')
#mod_auth.route('/signup/', methods=['GET', 'POST'])
def signup():
form = SignupForm()
if 'email' is session:
return redirect(url_for('profile'))
if request.method == 'POST':
if form.validate() == False:
return render_template("authentication/signup.html", form=form)
else:
new_user = User(form.username.data, form.email.data, form.password.data)
db.session.add(new_user)
db.session.commit()
session['email'] = new_user.email
return "Not found"
elif request.method == 'GET':
return render_template("authentication/signup.html", form=form)
#mod_auth.route('/signin/', methods=['GET', 'POST'])
def signin():
form = LoginForm(request.form)
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and check_password_hash(user.password, form.password.data):
session['user_id'] = user.id
flash('Welcome %s' % user.name)
return redirect(url_for('auth.home'))
flash('Wrong email or password', 'error-message')
return render_template("authentication/signin.html", form=form)
I got the same problem when I define my Model id as BIGINT, but my test code use sqlite as test database, when I change my Model defination, problem solved.
You can examine the database schema by starting the SQLite shell and using the .schema command.
$ sqlite3 app.db
sqlite> .schema user
At some point, your model had nullable=False set on the role column. You created the database with this model, then changed it to True. Changing the model after the database was created does not change the database, you need to migrate it. Use Alembic to migrate a SQLAlchemy database.
in flask project, if U use the sqlite database, If the id in the model is set to BIGINT, an error will be occured when do db.session.commit():UNIQUE constraint failed: tablename.column_name.
then you can resolve it by:
delete the table in the database, add the attribute AUTOINCREMENT, then recreate this table and be resolved:
CREATE TABLE table_name (
id BIGINT PRIMARY KEY AUTOINCREMENT NOT NULL,`
...