Title: SQLAlchemy in flask automap_base not mapping classes?
Tags: python-3.x,sqlite,flask-sqlalchemy,attributeerror

Question: long time listener first time caller. I'm making a flask app to create a small API off a SQLite db. Like the simplest thing in the world. I can query the db schema fine, so I know the engine is working. But as soon as I declare a class object with what is absolutely a class name, I get a AttributeError: 'function' object has no attribute 'WarID'. The error is so generic I haven't been able find anything. Help me, Obi-Wan Kenobi.
Here's the app.py
import numpy as np

import sqlalchemy
from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session
from sqlalchemy import create_engine, func

from flask import Flask, jsonify


#################################################
# Database Setup
#################################################
try:
    engine = create_engine("sqlite:///static/data/iWars.sqlite")

    # reflect an existing database into a new model
    Base = automap_base()
    # reflect the tables
    Base.prepare(autoload_with=engine)
    print("All about that base")
    print(Base)
    # # Save reference to the table
    # tribes = Base.classes.Tribes
    warsTable = Base.classes.Wars
    print("yes no maybe")
except Exception as e:
    print("Hey what's up looks like something gnarly happened")
    print(e)

#################################################
# Flask Setup
#################################################
app = Flask(__name__)


#################################################
# Flask Routes
#################################################

@app.route("/")
def welcome():
    """List all available api routes."""
    return (
        f"Available Routes:<br/>"
        # f"/api/v1.0/tribes<br/>"
        f"/api/v1.0/listwars<br/>"
    )

@app.route("/api/v1.0/listwars")
def warsRoute():
    # Create our session (link) from Python to the DB
    session = Session(engine)

    # Query all
    results = session.query(warsTable.WarID).all()

    session.close()

    # Create a dictionary from the row data and append to a list of all_passengers
    all_passengers = []
    for id in results:
        wars_dict = {}
        wars_dict["id"] = id
        all_passengers.append(wars_dict)

    return jsonify(all_passengers)


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

Here's the run
Notice that as soon as it hits the object declaration, it drops into the except block with just Wars as the exception, and then when I open the route I get the error shown.
(mlenv) ianmacsmacbook:USIndigenousWars ianmacmoore$ python app.py
All about that base
<class 'sqlalchemy.ext.automap.Base'>
Hey what's up looks like something gnarly happened
Wars
 Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with watchdog (fsevents)
All about that base
<class 'sqlalchemy.ext.automap.Base'>
Hey what's up looks like something gnarly happened
Wars
 * Debugger is active!
 * Debugger PIN: 955-667-953
127.0.0.1 - - [11/Mar/2023 13:19:44] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [11/Mar/2023 13:19:54] "GET /api/v1.0/listwars HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/opt/anaconda3/envs/mlenv/lib/python3.7/site-packages/flask/app.py", line 2464, in __call__
    return self.wsgi_app(environ, start_response)
  File "/opt/anaconda3/envs/mlenv/lib/python3.7/site-packages/flask/app.py", line 2450, in wsgi_app
    response = self.handle_exception(e)
  File "/opt/anaconda3/envs/mlenv/lib/python3.7/site-packages/flask/app.py", line 1867, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/opt/anaconda3/envs/mlenv/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/opt/anaconda3/envs/mlenv/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/opt/anaconda3/envs/mlenv/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/opt/anaconda3/envs/mlenv/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/opt/anaconda3/envs/mlenv/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/opt/anaconda3/envs/mlenv/lib/python3.7/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/opt/anaconda3/envs/mlenv/lib/python3.7/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/ianmacmoore/Documents/BluePlusRed/USIndigenousWars/app.py", line 56, in warsRoute
    results = session.query(warsTable.WarID).all()
NameError: name 'warsTable' is not defined
127.0.0.1 - - [11/Mar/2023 13:19:54] "GET /api/v1.0/listwars?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
127.0.0.1 - - [11/Mar/2023 13:19:54] "GET /api/v1.0/listwars?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
127.0.0.1 - - [11/Mar/2023 13:19:54] "GET /api/v1.0/listwars?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
127.0.0.1 - - [11/Mar/2023 13:19:54] "GET /api/v1.0/listwars?__debugger__=yes&cmd=resource&f=ubuntu.ttf HTTP/1.1" 200 -
127.0.0.1 - - [11/Mar/2023 13:19:54] "GET /api/v1.0/listwars?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
127.0.0.1 - - [11/Mar/2023 13:40:37] "GET /api/v1.0/wars?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
127.0.0.1 - - [11/Mar/2023 13:40:37] "GET /api/v1.0/wars?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
127.0.0.1 - - [11/Mar/2023 13:40:37] "GET /api/v1.0/wars?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
127.0.0.1 - - [11/Mar/2023 13:40:37] "GET /api/v1.0/wars?__debugger__=yes&cmd=resource&f=ubuntu.ttf HTTP/1.1" 200 -

Here're the class definitions used to initially write to the db
class Against(Base):
    __tablename__ = 'Against'
    TribeID = Column(Integer, primary_key=True)
    AgainstCode = Column(Integer)
    WarID = Column(Integer)

class Tribes(Base):
    __tablename__ = 'Tribes'
    TribeID = Column(Integer, primary_key=True)
    TribeName = Column(String(255))
    TribeName2 = Column(String(255))
    TribeName3 = Column(String(255))

class Wars(Base):
    __tablename__ = 'Wars'
    WarID = Column(Integer, primary_key=True)
    WarName = Column(String(255))
    StartYear = Column(Integer)
    EndYear = Column(Integer)
    WikiLink = Column(String(255))
    LengthYears = Column(Integer)

class YearSum(Base):
    __tablename__ = 'YearSum'
    Year = Column(Integer, primary_key=True)
    SumWars = Column(Integer)
    y = Column(Integer)

And here's the table object being created from a Jupyter Notebook on the same db
wars = Table("Wars", metadata_obj, autoload_with=engine)
wars

Table('Wars', MetaData(bind=None), Column('index', BIGINT(), table=<Wars>), Column('WarID', BIGINT(), table=<Wars>), Column('War Name', TEXT(), table=<Wars>), Column('StartYear', BIGINT(), table=<Wars>), Column('EndYear', BIGINT(), table=<Wars>), Column('WikiLink', TEXT(), table=<Wars>), Column('LengthYears', BIGINT(), table=<Wars>), schema=None)

Here's what I'm running
conda list '^(python|flask|sqlal)'
# packages in environment at /opt/anaconda3/envs/mlenv:
#
# Name                    Version                   Build  Channel
flask                     1.1.2              pyhd3eb1b0_0  
python                    3.7.13               hdfd78df_0  
python-dateutil           2.8.2              pyhd3eb1b0_0  
python-fastjsonschema     2.16.2           py37hecd8cb5_0  
python-libarchive-c       2.9                pyhd3eb1b0_1  
python-lsp-black          1.2.1            py37hecd8cb5_0  
python-lsp-jsonrpc        1.0.0              pyhd3eb1b0_0  
python-lsp-server         1.5.0            py37hecd8cb5_0  
python-slugify            5.0.2              pyhd3eb1b0_0  
python-snappy             0.6.0            py37h23ab428_3  
python.app                3                py37hca72f7f_0  
sqlalchemy                1.4.39           py37hca72f7f_0

Thanks for your time and attention.

Answer: The declaration of wars the automapped class and wars the route function are both declared in the module's top-level namespace, so the declaration of the route function overwrites the automapped class.
The error message

'function' object has no attribute 'WarID'

refers to the route function, not the automapped class.
Choose a different name for one of them, that does not conflict with any other names in the namespace.