Create the Expense Table Model
Define Expense as a SQLModel table model and move it to a dedicated models.py file
Writing code and entering commands is only available on desktop. Open this page on a larger screen to complete this chapter.
Moving models to their own file
Your Expense, ExpenseUpdate, and ExpenseOut classes currently live in main.py. As your project grows to multiple files — main.py, database.py, models.py — keeping models in main.py creates circular imports: database.py needs the models, main.py needs the database, and you end up with everything importing everything else.
Putting models in models.py breaks that cycle. Both main.py and database.py can import from models.py without importing from each other.
What we changed for you
We went ahead and migrated the models to models.py. The imports, the validator, and ExpenseUpdate are already in place. Two things are still missing — the two changes that matter most.
From BaseModel to SQLModel
The base class needs to change:
# Before
class Expense(BaseModel):
# After
class Expense(SQLModel, table=True):table=True tells SQLModel to create a matching table in the database. Every field in the class becomes a column. Pydantic validation still works — SQLModel extends BaseModel, so Field(min_length=1), Field(gt=0), and @field_validator all behave exactly as before.
The id field
The old Expense model had no id field — the application assigned IDs using the counter variable. With a database, IDs are assigned by the database itself:
id: Optional[int] = Field(default=None, primary_key=True)Optional[int]— the field can beNonebefore the record is saveddefault=None— you do not provide an ID when creating an expense; the database fills it inprimary_key=True— this column uniquely identifies each row
ExpenseOut is gone
ExpenseOut was a separate model used as the response type for endpoints. Because Expense(SQLModel, table=True) is both a Pydantic model and a database table, it handles serialization too. One class, three roles.
Instructions
Two changes turn Expense from a plain data class into a database table.
- On the
class Expense(SQLModel):line, addtable=Trueso it readsclass Expense(SQLModel, table=True):. This tells SQLModel to create a matching table in the database. - Add
id: Optional[int] = Field(default=None, primary_key=True)as the first field inside theExpenseclass, right abovedescription. This lets the database assign IDs automatically — you do not provide one when creating an expense.
Interactive Code Editor
Sign in to write and run code, track your progress, and unlock all chapters.
Sign In to Start Coding