Premier League Predictor: FastAPI & PostgreSQL — Chapter 11, Exercise 3 ==================================================== TASK Explain what Base.metadata.create_all actually does and doesn't do, and describe what would genuinely need to happen to add a new column to the Fixture table after this app is already deployed and running. SOLUTION Base.metadata.create_all(bind=engine) inspects every model class registered on Base (Team, Season, SeasonTeam, Gameweek, Fixture, Prediction) and, for each one, issues a real CREATE TABLE statement — but only for tables that don't already exist in the connected database. If a table with the matching name is already present, create_all leaves it completely untouched; it never inspects that existing table's own columns, never compares them against the current model definition, and never issues an ALTER TABLE to reconcile any difference between them. That means adding a new field to the Fixture model in models.py — say, a nullable attendance column — and simply re-running create_all against the already-deployed database would do nothing at all to the real fixtures table already sitting in PostgreSQL. The Python model would now expect a column the actual database table doesn't have, and any query touching that new field would fail against the real, unmodified table structure. To genuinely add that column after deployment, one of two real things would have to happen: either a person manually runs a real ALTER TABLE fixtures ADD COLUMN attendance INTEGER; statement directly against the production database (a change create_all was never designed to make), or the project adopts a real schema-migration tool like Alembic, which is specifically built to generate and apply exactly that kind of ALTER TABLE change in a controlled, versioned way — a real tool this course deliberately doesn't build, leaving it as an honest, named gap rather than something create_all can quietly handle on its own. WHY THIS WORKS AS AN ANSWER ---------------------------- It correctly distinguishes what create_all actually checks (table existence only) from what it doesn't do (compare or alter existing table structure), and names both real, concrete paths — a manual ALTER TABLE or adopting Alembic — that would actually be needed to add a column to an already-deployed table.