Search code examples
pythonsqliteflaskflask-sqlalchemy

How do I use flask-sqlalchemy to connect flask to a database?


I first followed this tutorial: https://python-adv-web-apps.readthedocs.io/en/latest/flask_db1.html Which worked (I got a website with the output 'It Works')

I then tried to follow the subsequent tutorial: https://python-adv-web-apps.readthedocs.io/en/latest/flask_db2.html

However, I am now getting the error: '((sqlite3.OperationalError) no such table:)'

Which stems from the following code:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
#from sqlalchemy.sql import text

app=Flask(__name__)

db_name='Monsters.db'

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + db_name

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

# this variable, db, will be used for all SQLAlchemy commands
db = SQLAlchemy(app)

class Creature(db.Model):
    __tablename__ = 'Creatures'
    ID = db.Column(db.Integer, primary_key=True)
    Name = db.Column(db.Text)
    AC = db.Column(db.Integer)
    HP = db.Column(db.Integer)
    
@app.route('/')
def index():
    try:
        Creatures = Creature.query.order_by(Creature.Name).all()
        print(Creatures)
        sock_text = '<ul>'
        
        for Monster in Creatures:
            sock_text += '<li>' + Creatures.Name + '</li>'
        sock_text += '</ul>'
        return sock_text
    
    except Exception as e:
        
        # e holds description of the error
        error_text = "<p>The error:<br>" + str(e) + "</p>"
        hed = '<h1>Something is off.</h1>'
        
        return hed + error_text

if __name__ == '__main__':
    app.run(debug=True, port=5000)

I checked my table in DB Browser (SQLite) and all the names are correct. I also looked at the instance folder generated by the code. It also had a Monsters.db file in it but using DB Browser to open this gives just an empty folder; while replacing this with the actual Monsters.db file gives the error "'list' object has no attribute 'Name'" but this feels like something I shouldn't have to do.

Update: I have updated my code using the following guide - https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/quickstart/#create-the-tables

However I am unsure if I am calling SQLAlchemy.create_all() correctly, as it doesn't give a lot of context for where it should be used. Additionally, it does now create this database in my instance fiolder but it is empty which does mean I no longer have an error but my webpage is blank.

So, how do I call SQLAlchemy.create_all() in a way that gives me a populated database in my instance folder?

Update 2 - Final Update:

I realised by reading the documentation that to read from a database table already containing data the reflect() method is required as described here: https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/models/

This led me to find this useful answer, describing how to do just that: How to build a flask application around an already existing database?

Which I was able to combine with the code from the outdated tutorial to get the expected result. The final code is shown below:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base 

db_name='Monsters.db'
engine=create_engine('sqlite:///' + db_name, echo=False)

Base=declarative_base()
Base.metadata.reflect(engine)

class Creatures(Base):
    __table__ = Base.metadata.tables['Creatures']
    
from sqlalchemy.orm import scoped_session, sessionmaker

from flask import Flask
    
app=Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + db_name
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    
@app.route('/')
def index():
    try:
        
        db_session=scoped_session(sessionmaker(bind=engine))
        Creature=db_session.query(Creatures.ID, Creatures.Name, Creatures.HP, Creatures.AC)
        
        sock_text = '<ul>'
        
        for Monster in Creature:
            sock_text += '<li>' + Monster.Name + '</li>'
        sock_text += '</ul>'
        
        return sock_text
    
    except Exception as e:
        
        # e holds description of the error
        error_text = "<p>The error:<br>" + str(e) + "</p>"
        hed = '<h1>Something is off.</h1>'
        
        return hed + error_text

if __name__ == '__main__':
    app.run(debug=True, port=5000)    

Solution

  • while replacing this with the actual Monsters.db file gives the error "'list' object has no attribute 'Name'"

    for Monster in Creatures:
        sock_text += '<li>' + Creatures.Name + '</li>'
    

    Presumably that is supposed to be Monster.Name, not Creatures.Name.