sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table
I just got done setting up a Flask app and I dealt with this kind of problem.
I strongly suspect the problem here is that the instance of db
that you are creating in __init__.py
is unaware of the contents of models.py
, including the User
class. The db
object in __init__.py
is a totally separate object from the db
you are creating in models.py
. So when you run db.create_all()
in __init__.py
, it is checking the list of tables that it knows about and isn't finding any. I ran into this exact issue.
What I discovered is that the models (like User
) are registered with the particular db
object that is listed in the model's class definition (e.g. class User(db.Model):
).
So basically my understanding is that the way to fix this is to run db.create_all()
using the same instance of db
that is being used to define the models. In other words, run db.create_all()
from within models.py
.
Here's my code so you can see how I have it set up:
app.py
:
#!flask/bin/pythonimport osfrom flask import Flaskclass CustomFlask(Flask): jinja_options = Flask.jinja_options.copy() jinja_options.update(dict( variable_start_string='%%', # Default is '{{', I'm changing this because Vue.js uses '{{' / '}}' variable_end_string='%%', ))app = CustomFlask(__name__)app.config['SECRET_KEY'] = 'hard to guess string'import yamlif os.environ['SERVER_ENVIRONMENT'] == 'PRODUCTION': config_filename = "production.yaml"elif os.environ['SERVER_ENVIRONMENT'] == 'LOCAL': config_filename = "local.yaml"else: config_filename = "local.yaml"base_directory = path = os.path.dirname(os.path.realpath(__file__))with open(base_directory + "/config/" + config_filename) as config_file: config = yaml.load(config_file)db_config = config['database']SQLALCHEMY_DATABASE_URI = "mysql+mysqlconnector://{username}:{password}@{hostname}/{databasename}".format( username=db_config['username'], password=db_config['password'], hostname=db_config['hostname'], databasename=db_config['databasename'],)app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URIapp.config["SQLALCHEMY_POOL_RECYCLE"] = 299from flask_sqlalchemy import SQLAlchemydb = SQLAlchemy(app)db.app = appdef clear_the_template_cache(): app.jinja_env.cache = {}app.before_request(clear_the_template_cache)from flask_login import LoginManagerlogin_manager = LoginManager()login_manager.init_app(app)@login_manager.user_loaderdef load_user(email): from models import User return User.query.filter_by(email=email).first()if __name__ == '__main__': from routes import web_routes app.register_blueprint(web_routes) from api import api app.register_blueprint(api) # To get PyCharm's debugger to work, you need to have "debug=False, threaded=True" #app.run(debug=False, threaded=True) app.run(debug=True)
models.py
:
from app import dbimport datetimefrom werkzeug.security import generate_password_hash, \ check_password_hashclass Song(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String(80)) datetime_created = db.Column(db.DateTime, default=datetime.datetime.utcnow()) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) lines = db.relationship('Line', cascade="all,delete", backref=db.backref('song', lazy='joined'), lazy='dynamic') is_deleted = db.Column(db.Boolean, default=False)class Line(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) song_id = db.Column(db.Integer, db.ForeignKey('song.id')) spans_of_time = db.relationship('SpanOfTime', cascade="all,delete", backref=db.backref('line', lazy='joined'), lazy='dynamic')class SpanOfTime(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) line_id = db.Column(db.Integer, db.ForeignKey('line.id')) starting_64th = db.Column(db.Integer) # I'm assuming the highest-granularity desired will be a 1/64th note-length. length = db.Column(db.Integer) # I guess this'll be in 1/64th notes, so a 1/16th note will be '4'. content = db.Column(db.String(80))class User(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) email = db.Column(db.String(80), primary_key=True, unique=True) display_name = db.Column(db.String(80), default="A Rhymecraft User") password_hash = db.Column(db.String(200)) datetime_subscription_valid_until = db.Column(db.DateTime, default=datetime.datetime.utcnow() - datetime.timedelta(days=1)) datetime_joined = db.Column(db.DateTime, default=datetime.datetime.utcnow()) songs = db.relationship('Song', cascade="all,delete", backref=db.backref('user', lazy='joined'), lazy='dynamic') def __init__(self, email, password): self.email = email self.set_password(password) def __repr__(self): return '<User %r>' % self.email def set_password(self, password): self.password_hash = generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) def is_authenticated(self): return True def is_active(self): return True def is_anonymous(self): return False def get_id(self): return str(self.email)def init_db(): db.create_all() # Create a test user new_user = User('a@a.com', 'aaaaaaaa') new_user.display_name = 'Nathan' db.session.add(new_user) db.session.commit() new_user.datetime_subscription_valid_until = datetime.datetime(2019, 1, 1) db.session.commit()if __name__ == '__main__': init_db()
In your case, require to add following code into __init__.py:
from models import User, Role@app.shell_context_processordef make_shell_context(): return dict(db=db, User=User, Role=Role)
then you do your previous works, it's all work.