I am creating a Flask app, and I have a page with a form to register new users. But when I type some data, I get the error sqlalchemy.exc.OperationalError: (OperationalError) no such table: user. In the file config.py I have this line
SQLALCHEMY_DATABASE_URI = "sqlite:///example.sqlite" and this is the rest of my code:
flask_start.py
import os
from flask import Flask
from config import Config
from flask_utils import page_bp
from db_creation import db
def create_app():
appname = "IOT - PROJECT NASTIS"
app = Flask(appname)
myconfig = Config
app.config.from_object(myconfig)
app.register_blueprint(page_bp, url_prefix='')
db.init_app(app)
return app
def setup_database(app):
with app.app_context():
db.create_all()
if __name__ == "__main__":
app = create_app()
if not os.path.isfile('/tmp/test.sqlite'):
setup_database(app)
port = 8000
interface = '0.0.0.0'
app.run(host=interface, port=port, debug=True)
db_creation.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
flask_utils.py
db_creation import db
from User import User
page_bp = Blueprint("page_bp", __name__)
#page_bp.route('/mainPage/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm(request.form)
if request.method == 'POST' and form.validate():
user = User(form.username.data, form.email.data,
sha256_crypt.encrypt(str(form.password.data)))
db.session.add(user)
db.session.commit()
flash('Thanks for registering, you can now log in!!', 'success')
return redirect(url_for('page_bp.register'))
return render_template('register.html', form=form)
User.py
from db_creation import db
class User(db.Model):
username = db.Column(db.String(100), primary_key=True)
email = db.Column(db.String(100))
password = db.Column(db.String(100))
def __init__(self, username, email, password):
self.username = username
self.email = email
self.password = password
What am I doing wrong? I can't figure out what is the mistake
When you run db.create_all() it needs to know which tables to create-- it can tell that by which models it currently can see.
User isn't imported in your start_flask.py file-- so when db.create_all() is run it pretty much goes 'huh, I'm not aware of any models, I guess I'll just create an empty database with no tables' it doesn't know to go and look in other files, so you just need to introduce them :)
Adding from User import User to the imports in the start_flask.py file should solve that.
Also-- you're checking for a file in /tmp/... to trigger your 'no database file yet' check, but your SQLALCHEMY_DATABASE_URI isn't matching that.
Related
Help!
I'm really new to flask and I've been muddling along with all the tutorials I could find but I've run into a problem I can't figure out.
Here's my error:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: User
[SQL: SELECT "User"."userId" AS "User_userId", "User".email AS "User_email", "User".password AS "User_password", "User".username AS "User_username", "User".lastlogin AS "User_lastlogin", "User".isauthenticated AS "User_isauthenticated"
FROM "User"
WHERE "User".email = ?
LIMIT ? OFFSET ?]
[parameters: ('test#example.com', 1, 0)]
(Background on this error at: http://sqlalche.me/e/13/e3q8)
So I'm working on the signup form and I've solved all the other bugs. But here what I can make out from this error is that my database or table isn't being created.
Here's my code:
init.py:
import os
from flask import Flask, render_template, redirect, session
from flask_login import LoginManager
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from werkzeug.utils import import_string
from complete.app import redirect_url
db = SQLAlchemy()
login_manager = LoginManager()
def create_app():
"""Create Flask application."""
app = Flask(__name__, instance_relative_config=False)
#app.context_processor
def inject_global_vars():
return dict(appname="Ebay Listing Viewer")
app.config.from_pyfile('configfile.py')
db.init_app(app)
login_manager.init_app(app)
with app.app_context():
from .userauth.auth import auth_bp
app.register_blueprint(auth_bp)
db.create_all()
from .models import User
#login_manager.user_loader
def load_user(user_id):
return User.get(user)
return app
models.py:
"""Data Models"""
from flask_login import UserMixin
from . import db
class User(UserMixin, db.Model):
__tablename__ = 'User'
userid = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(100), unique=True)
password = db.Column(db.String(200))
username = db.Column(db.String(1000))
lastlogin = db.Column(db.DateTime)
isauthenticated = db.Column(db.Boolean, default=False)
def is_active(self):
return True
def get_id(self):
return chr(self.userId)
def is_authenticated(self):
return self.isauthenticated
def is_anonymous(self):
return False
def __repr__(self):
return '<User> {}'.format(self.username)
def set_password(self, password):
self.password = generate_password_hash(
password,
method='sha256'
)
def check_password(self, password):
return check_password_hash(self.password, password)
config.py:
class Config(object):
"""Base config."""
SECRET_KEY = environ.get('SECRET_KEY')
STATIC_FOLDER = 'static'
TEMPLATES_FOLDER = 'templates'
class DevelopmentConfig(Config):
FLASK_ENV = 'development'
SQLALCHEMY_DATABASE_URI = environ.get('DEV_DATABASE_URI')
.env:
#among other things...
DEV_DATABASE_URI=sqlite:///db.sqlite
There is a file called db.sqlite in my root folder (ie the same directory as init.py) but I'm not sure it's got anything in it. When I try to open it, it shows The file is not displayed in the editor because it is either binary or uses an unsupported text encoding. (I'm using VSCode).
I'm not quite sure how the database uri works - I've had a lot of looks around Google and everyone seems to think it's completely obvious. Is it the file path to my database file? Do I create the database file or is it automatically created?
I'm thinking the problem is likely to be that the database uri is wrong, but I dunno, I haven't got enough experience so maybe it's something completely different.
Thank you for taking the time to read and help!
I've created a Python Flask site with a login form and a signup form. Both of these are working and when a user signs up, their email, name and password (sha256 hashed) are stored in a sqlite database. I now need to use the flask_change_password library to create a form that will allow users to change their password and I'm just struggling on this.
First, I'm using PyCharm and installed flask-change-password for my env but when I add this line from flask_change_password import ChangePassword, I get:
from flask_change_password import ChangePassword
ModuleNotFoundError: No module named 'flask_change_password'
I don't understand this because I did install it in my env. I also tried installing with pip pip install flask-change-password to resolve the error without success.
My second problem is that I don't know where I should implement or how to implement the change_password form or how to change a certain password for a specific user.
This is my auth code for signup and login:
from flask import Blueprint, render_template, redirect, url_for, request, flash
from flask_change_password import ChangePasswordForm
from flask_login import login_user, login_required, logout_user
from sqlalchemy.sql.functions import current_user
from werkzeug.security import generate_password_hash, check_password_hash
from .models import User
from . import db
auth = Blueprint('auth', __name__)
#auth.route('/login')
def login():
return render_template('login.html')
#auth.route('/signup')
def signup():
return render_template('signup.html')
#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() # check to see if user already exists
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. please login with your email.')
return redirect(url_for('auth.signup'))
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'))
#auth.route('/login', methods=['POST'])
def login_post():
email = request.form.get('email')
password = request.form.get('password')
remember = True if request.form.get('remember') else False
user = User.query.filter_by(email=email).first()
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')) # if the user doesn't exist or password is wrong, reload the page
login_user(user, remember=remember)
return redirect(url_for('main.profile'))
#auth.route('/logout')
#login_required
def logout():
logout_user()
return render_template('goodbye.html')
My init code:
from flask_sqlalchemy import SQLAlchemy
from flask_change_password import ChangePassword
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = 'UMGC-SDEV300-Key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
# app.secret_key = os.urandom(20)
# flask_change_password = ChangePassword(min_password_length=10, rules=dict(long_password_override=2))
# flask_change_password.init_app(app)
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):
return User.query.get(int(user_id))
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
As you can see, I'm importing the flask_change_password module ChangePasswordForm but It fails to import??
I ended up fixing this issue by removing flask from the default instance of pip3. I also removed flask-security from the default instance of pip3. I then reinstalled each of these modules in the venv and everything worked.
I'm trying to split out my models into a separate file as it's getting too large to manage. I've followed this but I am getting a NameError despite having run db.create_all():
NameError: name 'importsTable' is not defined
# stack_app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from stack_dbmodels import db
from stack_dbmodels import importsTable
from datetime import datetime
app = Flask(__name__)
app.secret_key = 'secretsquirrel'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///stack_newAppCsv.db'
db.init_app(app)
#app.route('/', methods=['GET'])
def stack():
username = "username"
new_user = importsTable(username)
db.session.add(new_user)
db.session.commit()
return "Done!"
if __name__ == "__main__":
app.run(debug = True, port=8080)
My models file:
# stack_dbmodels.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class importsTable(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80))
def __init__(self, username):
self.username = username
def __repr__(self):
return '<Import {0}>'.format(self.username)
The problem was mainly with the context of the app but during my tests there were a couple of gotchas I found. Key to make the above work was:
When I reference importsTable it wasn't initially defined unless I did:
from stack_dbmodels import db
from stack_dbmodels import importsTable
And whilst the .db file was created, the context wasn't correct so it couldn't create the actual importsTable so I added the following and problem solved, the key bit was "with app.app_context()":
db.init_app(app)
with app.app_context():
db.create_all()
Final working code was:
# stack_app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from stack_dbmodels import db
from stack_dbmodels import importsTable
from datetime import datetime
app = Flask(__name__)
app.secret_key = 'secretsquirrel'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///stack_newAppCsv.db'
db.init_app(app)
with app.app_context():
db.create_all()
#app.route('/', methods=['GET'])
def stack():
username = "username"
new_user = importsTable(username)
db.session.add(new_user)
db.session.commit()
return "Done!"
if __name__ == "__main__":
app.run(debug = True, port=8080)
I am trying to run my create_db.py file to create the posts.db database, but it will not get created in my project directory. And when I run the main file for the blog and try to login in I get the error below.
I have looked up this error and seen that other people have gotten it as well and asked about it here on Stackoverflow, but none of them seems to help me. I have read that this could be because something in my blog.py file is running main before the database gets created. But, I am thinking that it has something to do with the configuration. Mainly the PATH of the database could be getting mixed up with the app.config['SQLAlCHEMY_DATABASE_URI'] = 'sqlite:///' line.
Any ideas?
Here is the error
sqlalchemy.exc.OperationalError
OperationalError: (OperationalError) no such table: posts u'SELECT posts.id AS posts_id, posts.title AS posts_title, posts.post AS posts_post \nFROM posts' ()
OperationalError: (OperationalError) no such table: posts u'SELECT posts.id AS posts_id, posts.title AS posts_title, posts.post AS posts_post \nFROM posts' ()
Here is my code. There are three files here: blog.py, models.py, create_db.py
blog.py
# controller of blog app
from flask import Flask, render_template, request, session,\
flash, redirect, url_for, g
from flask.ext.sqlalchemy import SQLAlchemy
import sqlite3
from functools import wraps
# create the application object
app = Flask(__name__)
# configuration
app.secret_key = 'x13xa8xf5}[xfexd4Zxb8+x07=7xc9xe1Bxcfxbdt.ox87oxc9'
app.config['SQLAlCHEMY_DATABASE_URI'] = 'sqlite:///'
# create sqlalchemy object
db = SQLAlchemy(app)
from models import *
# login required decorator
def login_required(test):
#wraps(test)
def wrap(*args, **kwargs):
if 'logged_in' in session:
return test(*args, **kwargs)
else:
flash('You need to login first.')
return redirect(url_for('login'))
return wrap
#app.route('/', methods = ['GET','POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != 'admin' or request.form['password'] != 'admin':
error = 'Invalid Credentils. Please try again.'
else:
session['logged_in'] = True
return redirect(url_for('main'))
return render_template('login.html', error=error)
#app.route('/main')
#login_required
def main():
posts = db.session.query(BlogPost).all()
return render_template('main.html', posts=posts)
#app.route('/add', methods=['POST'])
#login_required
def add():
title = request.form['title']
post = request.form['post']
if not title or not post:
flash("All fields are required. Please try again.")
return redirect(url_for('main'))
else:
db.session.add(title)
db.session.add(post)
db.session.commit()
db.session.close()
flash('New entry was successfully posted!')
return redirect(url_for('main'))
#app.route('/logout')
def logout():
session.pop('logged_in', None)
flash('You were logged out')
return redirect(url_for('login'))
def connect_db():
return sqlite.connect('posts.db')
if __name__ == '__main__':
app.run(debug = True)
models.py:
from blog import db
class BlogPost(db.Model):
__tablename__ = "posts"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String, nullable=False)
post = db.Column(db.String, nullable=False)
def __init__(self, title, post):
self.title = title
self.post = post
def __repr__(self):
return '<title {}'.format(self.title)
create_db.py
from blog import db
from models import BlogPost
# create the database and the db tables
db.create_all()
# insert
db.session.add(BlogPost("Good","I\'m good."))
db.session.add(BlogPost("Well","I\'m well."))
db.session.add(BlogPost("Post","I\'m a post."))
# commit the changes
db.session.commit()
There's a typo in SQLAlCHEMY_DATABASE_URI, should be SQLALCHEMY_DATABASE_URI, the 2nd l.
When running from blog import db some statements in blog.py get executed, including the one with sqlite:/// which is where the path is set. Modifying this line
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///posts.db'
and then after running create_db.py should create a database in app's root directory. There doesn't seem to be another place in the code where the db path is set.
It looks like you may also run into some circular import problems (will throw ImportError: cannot import name [name]). Some solutions are putting the app init stuff like db = SQLAlchemy.. into a separate file or importing at the end of the file.
I'm trying to integrate PostgreSQL and SQLAlchemy but SQLAlchemy.create_all() is not creating any tables from my models.
My code:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://login:pass#localhost/flask_app'
db = SQLAlchemy(app)
db.create_all()
db.session.commit()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '<User %r>' % self.username
admin = User('admin', 'admin#example.com')
guest = User('guest', 'guest#example.com')
db.session.add(admin)
db.session.add(guest)
db.session.commit()
users = User.query.all()
print users
But I get this error: sqlalchemy.exc.ProgrammingError: (ProgrammingError) relation "user" does not exist
How can I fix this?
You should put your model class before create_all() call, like this:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://login:pass#localhost/flask_app'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '<User %r>' % self.username
with app.app_context():
db.create_all()
db.session.add(User('admin', 'admin#example.com'))
db.session.add(User('guest', 'guest#example.com'))
db.session.commit()
users = User.query.all()
print(users)
If your models are declared in a separate module, import them before calling create_all().
Say, the User model is in a file called models.py,
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://login:pass#localhost/flask_app'
db = SQLAlchemy(app)
# See important note below
from models import User
with app.app_context():
db.create_all()
db.session.add(User('admin', 'admin#example.com'))
db.session.add(User('guest', 'guest#example.com'))
db.session.commit()
users = User.query.all()
print(users)
Important note: It is important that you import your models after initializing the db object since, in your models.py you also need to import the db object from this module.
If someone is having issues with creating tables by using files dedicated to each model, be aware of running the "create_all" function from a file different from the one where that function is declared.
So, if the filesystem is like this:
Root
--app.py <-- file from which app will be run
--models
----user.py <-- file with "User" model
----order.py <-- file with "Order" model
----database.py <-- file with database and "create_all" function declaration
Be careful about calling the "create_all" function from app.py.
This concept is explained better by the answer to this thread posted by #SuperShoot
This is probably not the main reason why the create_all() method call doesn't work for people, but for me, the cobbled together instructions from various tutorials have it such that I was creating my db in a request context, meaning I have something like:
# lib/db.py
from flask import g, current_app
from flask_sqlalchemy import SQLAlchemy
def get_db():
if 'db' not in g:
g.db = SQLAlchemy(current_app)
return g.db
I also have a separate cli command that also does the create_all:
# tasks/db.py
from lib.db import get_db
#current_app.cli.command('init-db')
def init_db():
db = get_db()
db.create_all()
I also am using a application factory.
When the cli command is run, a new app context is used, which means a new db is used. Furthermore, in this world, an import model in the init_db method does not do anything, because it may be that your model file was already loaded(and associated with a separate db).
The fix that I came around to was to make sure that the db was a single global reference:
# lib/db.py
from flask import g, current_app
from flask_sqlalchemy import SQLAlchemy
db = None
def get_db():
global db
if not db:
db = SQLAlchemy(current_app)
return db
I have not dug deep enough into flask, sqlalchemy, or flask-sqlalchemy to understand if this means that requests to the db from multiple threads are safe, but if you're reading this you're likely stuck in the baby stages of understanding these concepts too.