I've setup a flask site and I'm trying to get a signup page to work. The page itself renders but when I enter information in the form and submit I get a sqlalchemy.exc.OperationalError. Any help here would be greatly appreciated!!
Here is my signup function:
#auth.route('/signup', methods=['POST'])
def signup_post():
email = request.form.get('email')
name = request.form.get('name')
password = request.form.get('password')
user = User.query.filter_by(email=email).first() # if this returns a user,
# then the email already exists in database
if user: # if a user is found, we want to redirect back to signup page so user can try again
flash('email address already exists')
return redirect(url_for('auth.signup'))
# create a new user with the form data. Hash the password so the plaintext version isn't saved.
new_user = user(email=email, name=name, password=generate_password_hash(password, method='sha256'))
# add the new user to the database
db.session.add(new_user)
db.session.commit()
return redirect(url_for('auth.login'))
The specific error says no such table: user then:
[SQL: SELECT user.id AS user_id, user.email AS user_email, user.password AS user_password, user.name AS user_name
FROM user
WHERE user.email = ?
LIMIT ? OFFSET ?]
[parameters: ('', 1, 0)]
(Background on this error at: http://sqlalche.me/e/13/e3q8)
Here is where I initialize the DB and create the app:
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = 'UMGC-SDEV300-Key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
db.init_app(app)
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
from .models import User
#login_manager.user_loader
def load_user(user_id):
# since the user_id is just the primary key of our user table, use it in the query for the user
return User.query.get(int(user_id))
# blueprint for auth routes in our app
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
# blueprint for non-auth parts of app
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
Check the models.py and add the line: tablename = 'user'
class User(UserMixin, db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy
firstname = db.Column(db.String(30))
lastname = db.Column(db.String(30))
email = db.Column(db.String(100), uni
And in the init.py change to
#login_manager.user_loader
def load_user(user_id):
return User.query.filter_by(id=user_id).first()
And make sure that file exits,and have the table: user
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
I figured it out. I just needed to create the database. I went through this step once when I first created the application but I guess I missed something. I deleted the database and created a new one using the below command and that did the trick.
from project import db, create_app
db.create_all(app=create_app())
This was done from the Python REPL.
Related
I have a flask app that acts as a login system. I am trying to use #roles_required() decorator in order to check if the user is an admin, and if they are, giving them access to specific areas.
When I try log in with the correct credentials, that have been created with the correct roles, it gives me the error below.
AttributeError: type object 'User' has no attribute 'get_user_by_token'
I have located the error, it seems to be that login_user() is not working properly. I have commented the line causing an error in login_post().
Let me know if there's anything else you need. Please help!!! No one can seem to figure it out...
app.py
from flask import Flask, Blueprint, render_template, redirect, url_for, request, flash, session
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin, login_user, logout_user, login_required, current_user, LoginManager
import json
from flask_user import UserManager, roles_required
USER_ENABLE_EMAIL = False
auth = Blueprint('auth', __name__)
db = SQLAlchemy()
app = Flask(__name__)
app.config['SECRET_KEY'] = 'mysecretkey'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
db.init_app(app)
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
from app import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
app.config['USER_ENABLE_EMAIL'] = False
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy
username = db.Column(db.Text, unique=True, nullable=False)
password = db.Column(db.Text, nullable=False)
# Relationships
roles = db.relationship('Role', secondary='user_roles',
backref=db.backref('users', lazy='dynamic'))
# Define the Role data model
class Role(db.Model):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(50), unique=True)
# Define the UserRoles data model
class UserRoles(db.Model):
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('user.id', ondelete='CASCADE'))
role_id = db.Column(db.Integer(), db.ForeignKey('role.id', ondelete='CASCADE'))
user_manager = UserManager(app, db, User)
#auth.route('/')
def login():
return render_template('login.html')
#auth.route('/login', methods=['POST'])
def login_post():
username = request.form.get('username')
password = request.form.get('password')
remember = True if request.form.get('remember') else False
adminuser = 'adminuser'
adminpwd = 'adminpwd'
if username == adminuser:
if check_password_hash(adminpwd, password):
login_user(user, remember=remember) # this is the problem
return redirect('/admin/dashboard')
if not user or not check_password_hash(user.password, password):
flash('Please check your login details and try again.')
return redirect(url_for('auth.login'))
login_user(user, remember=remember) # this is the problem
return redirect(url_for('auth.profile'))
#auth.route('/profile')
#login_required
#roles_required('userClass')
def profile():
return render_template('profile.html')
#auth.route('/admin/dashboard')
#roles_required('Admin')
def admin_dashboard():
return render_template("admin_dashboard.html")
*I am trying to create a login page for my website using Flask sqlalchemy and sqlite3 to create the login user database. I am able to get the website up and running just fine, but am unable to successfully create a user within my database. I have create a table for my sqlite3 database using DB browser, and I have confirmed that it has the columns names that I want it to have using the command terminal on my Mac.
Each time I try to create a user I get the error:
sqlalchemy.exc.OperationalError sqlalchemy.exc.OperationalError: (sqlite3.OperationalError)
unable to open database file
The error message says that this line of code is causing all of my issues:
File "/Users/milesvonherrmann/Desktop/project /Cuisine.py", line 73, in signup Open an interactive python shell in this framedb.session.commit()
I have attached my Cuisine.py file below. How can I get my database file to be recognized?*
from flask import Flask, render_template, redirect, url_for
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
app = Flask(__name__)
app.config['SECRET_KEY'] = 'Thisissupposedtobesecret!'
app.config['SQLALCHEMY_DATABASE_URI'] =
'sqlite:///c/Users/milesvonherrmann/Desktop/project/usertable.db'
bootstrap = Bootstrap(app)
db = SQLAlchemy(app)
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)
email = db.Column(db.String(50), unique=True)
password = db.Column(db.String(80))
#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=4, max=15)])
password = PasswordField('password', validators=[InputRequired(), Length(min=8, max=80)])
remember = BooleanField('remember me')
class RegisterForm(FlaskForm):
email = StringField('email', validators=[InputRequired(), Email(message='Invalid email'),
Length(max=50)])
username = StringField('username', validators=[InputRequired(), Length(min=4, max=15)])
password = PasswordField('password', validators=[InputRequired(), Length(min=8, 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 '<h1>' + form.username.data + ' ' + form.password.data + '</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, method='sha256')
new_user = User(username=form.username.data, email=form.email.data, password=hashed_password)
db.session.add(new_user)
db.session.commit()
return '<h1>New user has been created!</h1>'
#return '<h1>' + form.username.data + ' ' + form.email.data + ' ' + form.password.data + '</h1>'
return render_template('signup.html', form=form)
#app.route('/dashboard')
#login_required
def dashboard():
return render_template('dashboard.html', name=current_user.username)
#app.route('/logout')
#login_required
def logout():
logout_user()
return redirect(url_for('index'))
#if __name__ == '__main__':
#app.run(debug=True)
I would like to add another answer to this question.
I had the same problem and I found out something. First check the parent path in your console. In your case, if you put the whole path, then you need to put 4 "/".
So it would be like this:
app.config['SQLALCHEMY_DATABASE_URI'] =
'sqlite:////c/Users/milesvonherrmann/Desktop/project/usertable.db'
I suppose the C refers to the unit so you would need to add the column as well.
When you changed to app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///usertable.db'
It worked because it only needs 3 "/" when using partial path. I also prefer this option my problem was that I had a different parent path and it was looking somewhere else.
I think you have wrong the config path for the database. Try this:
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///usertable.db'
I am always doing it this way. Hope that it will work
For my web application, I'm using Flask-Dance to have users create their account by only logging into their Google accounts (I don't want them to have to make any new usernames or passwords). When I first tried doing this, I ran into the error:
Cannot get OAuth token without an associated user
I found another post that dealt with this issue (Link to it: flask_dance: Cannot get OAuth token without an associated user). The #3 part of the answer is what I'm trying to do, but as I'm trying to "manually create the user account and associate it with the OAuth token," I'm getting the following error from SQLAlchemy:
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) datatype mismatch
I'm almost sure that this is coming from the token. I have no clue why it won't accept the token I'm passing to it. If I'm able to resolve this error, I think the code will manually create the user account and associate it with the OAuth token, and then I can get rid of (as the other post says I should):
"user_required=False" in the line:
google_blueprint.backend = SQLAlchemyBackend(OAuth, db.session, user=current_user, user_required=False)
Right now I need help fixing the error with the database, but if you see any other potential errors that would be very helpful. I'm new to web development as I'm trying to teach myself this process. Since this error can be rooted from anywhere in my code, I'm attaching my python file below.
import os
import json
import datetime
from flask import Flask, redirect, url_for
from flask_dance.contrib.google import make_google_blueprint, google
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin, current_user, LoginManager, login_required, login_user, logout_user
from flask_dance.consumer.backend.sqla import OAuthConsumerMixin, SQLAlchemyBackend
from flask_dance.consumer import oauth_authorized
from sqlalchemy.orm.exc import NoResultFound
app = Flask(__name__)
app.config['SECRET_KEY'] = 'thisissupposedtobeasecret'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////Users/kentjo/Documents/git/CareerDay/login.db'
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = '1'
os.environ["OAUTHLIB_RELAX_TOKEN_SCOPE"] = '1'
google_blueprint = make_google_blueprint(
client_id='#removed for security of this post#',
client_secret='#removed for security of this post#',
scope=['profile', 'email'],
offline=True #added in
)
app.register_blueprint(google_blueprint, url_prefix='/google_login')
db = SQLAlchemy(app)
login_manager = LoginManager(app)
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(250), unique=True)
first_name = db.Column(db.String(250), unique=False)
last_name = db.Column(db.String(250), unique=False)
time_stamp = db.Column(db.String(25), unique=False)
first_choice = db.Column(db.String(250), unique=False)
second_choice = db.Column(db.String(250), unique=False)
third_choice = db.Column(db.String(250), unique=False)
fourth_choice = db.Column(db.String(250), unique=False)
fifth_choice = db.Column(db.String(250), unique=False)
submitted = db.Column(db.Boolean, unique=False, default=True)
class OAuth(OAuthConsumerMixin, db.Model):
user_id = db.Column(db.Integer, db.ForeignKey(User.id))
user = db.relationship(User)
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
google_blueprint.backend = SQLAlchemyBackend(OAuth, db.session, user=current_user, user_required=False) #Eventually need to remove user_required=False
#app.route('/google')
def google_login():
if not google.authorized:
return redirect(url_for('google.login'))
account_info = google.get('/plus/v1/people/me')
account_info_json = account_info.json()
json_str = json.dumps(account_info_json)
email = json.loads(json.dumps(json.loads(json_str)['emails']))[0]['value']
return '<h1>Your email is {}'.format(email)
#oauth_authorized.connect_via(google_blueprint)
def google_logged_in(blueprint, token):
account_info = blueprint.session.get('/plus/v1/people/me')
if account_info.ok:
account_info_json = account_info.json()
json_str = json.dumps(account_info_json)
google_email=json.loads(json.dumps(json.loads(json_str)['emails']))[0]['value']
google_first_name=json.loads(json.dumps(json.loads(json_str)['name']))['givenName']
google_last_name=json.loads(json.dumps(json.loads(json_str)['name']))['familyName']
google_id=json.loads(json.dumps(json.loads(json_str)['id']))
query = User.query.filter_by(email=google_email)
try:
user = query.one()
print("FOUND USER")
except NoResultFound:
print("NO USER FOUND: proceed to create")
user = User(
email=google_email,
first_name=google_first_name,
last_name=google_last_name
)
oauth = OAuth(
id=google_id,
provider=blueprint.name,
created_at=datetime.datetime.now(),
token=token, #PROBLEM COMING FROM HERE
user_id=user.id
)
oauth.user = user
db.session.add(user)
db.session.add(oauth)
db.session.commit()
login_user(user)
return False
#app.route('/')
#login_required
def index():
return '<h1>You are logged in as {}'.format(current_user.email)
#app.route('/logout')
#login_required
def logout():
logout_user()
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
I have read quite a bit of documentation and I can't see what is wrong with these lines
update_this = User.query.filter_by(email=email).first()
update_this.emailconfirmed = True
db.session.commit()
...and yet when I deploy the boolean column 'emailconfirmed' never is update to True. I have confirmed with print statements that update_this.emailconfirmed is False at the exact point in the code shown above... I just can't seem to update that value. Does anybody know what tests I can do, what imports I should check etc. etc.
Right now this is the top of my main .py file where the above code appears
from flask import Flask, render_template, request, session, redirect, url_for, make_response
# the following line imports from models.py
from models import db, User
# the following line imports SignupForm from forms.py
from forms import SignupForm, LoginForm
from flask_mail import Mail, Message
from itsdangerous import URLSafeTimedSerializer
# Production (causes Heroku to redirect to SSL)
from flask_sslify import SSLify
from flask_sqlalchemy import SQLAlchemy
import os
app = Flask(__name__)
sslify = SSLify(app)
sslify = SSLify(app, subdomains=True)
app.config.from_pyfile('config_file.cfg')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URL']
db = SQLAlchemy(app)
mail = Mail(app)
ts = URLSafeTimedSerializer(app.config['SECRET_KEY'], salt=app.config['SALT'])
and this is my models.py file
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)
firstname = db.Column(db.String(100))
lastname = db.Column(db.String(100))
role = db.Column(db.String(20))
roleapproved = db.Column(db.Boolean)
school = db.Column(db.String(100))
email = db.Column(db.String(120), unique=True)
emailconfirmed = db.Column(db.Boolean)
pwdhash = db.Column(db.String(100))
def __init__(self, firstname, lastname, role, school, email, password):
self.firstname = firstname.title()
self.lastname = lastname.title()
self.role = role.lower()
if role.lower() == 'student':
self.roleapproved = True
if role.lower() == 'teacher':
self.roleapproved = False
self.school = school.title()
self.email = email.lower()
self.set_password(password)
self.emailconfirmed = False
def set_password(self, password):
self.pwdhash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.pwdhash, password)
def __repr__(self):
return '<User {0}>'.format(self.email)
Any help on doing the update I mentioned above would be greatly appreciated!!
Ideally you want to maintain a single session throughout your application lifecycle. This way it makes it easy to reason about and you avoid binding sessions to individual models.
Thanks #Ilja Everila
In main.py instead of initializing SQLAlchemy
you should write,
db.init_app(app)
Define a save instance method for your User model.
def save(self):
"""Saves model object instance
"""
db.session.add(self)
db.session.commit()
You can call this method to save the instance as
update_this.save()
Another way to update the entity is to get the specific object session before committing
from sqlachemy.orm import session
...
session = session.object_session(update_this)
session.commit()
I've already configured SECURITY_PASSWORD_SALT and use "bcrypt". But every time i reload the page print encrypt_password('mypassword') will print different values, so i can't verify user input password via verify_password(form.password.data, user.password).
But I can login from flask-security built-in login view
Here is code to demonstrate the strange behavior of encrypt_password:
from flask import Flask, render_template
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.security import Security, SQLAlchemyUserDatastore, \
UserMixin, RoleMixin, login_required
from flask.ext.security.utils import encrypt_password, verify_password
# Create app
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'super-secret'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
app.config['SECURITY_PASSWORD_HASH'] = 'sha512_crypt'
app.config['SECURITY_PASSWORD_SALT'] = 'fhasdgihwntlgy8f'
# 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():
password = encrypt_password('mypassword')
print verify_password('mypassword', password)
return password
# return render_template('index.html')
if __name__ == '__main__':
app.run()
The fact that encrypt_password() generates a new value is by design. The fact that verify_password() fails is not. It's an already reported bug in Flask-Security.
When you use the login view, a different method, verify_and_update_password() is used instead, which doesn't suffer from the same problem.
The fix is not yet part of a new release. You can fix this issue yourself by applying the change from PR #223; it replaces the verify_password() function in the flask_security/utils.py file with:
def verify_password(password, password_hash):
"""Returns ``True`` if the password matches the supplied hash.
:param password: A plaintext password to verify
:param password_hash: The expected hash value of the password (usually form your database)
"""
if _security.password_hash != 'plaintext':
password = get_hmac(password)
return _pwd_context.verify(password, password_hash)
e.g. first hash the password with HMAC+SHA512 before verifying it against the hash, just as the original encrypt_password() does, and not apply encrypt_password() as the current released version does.