Store and Return the Expense
Assign an identifier, store the expense, and return it to the client
Writing code and entering commands is only available on desktop. Open this page on a larger screen to complete this chapter.
The global keyword
You defined counter = 0 at the top of your file, outside any function. This makes it a module-level variable — it exists for the entire program, not just inside one function.
Python functions can read module-level variables without any special syntax. But when you try to modify one with counter += 1, Python assumes you mean a local variable that was never defined and raises an UnboundLocalError.
The global keyword fixes this. It tells Python: "don't create a new local variable — use the existing module-level one."
def create_expense(expense: Expense):
global counter
counter += 1 # modifies the module-level counterYou only need global when you reassign the variable. Reading expenses[counter] works fine without it because you are modifying the dictionary's contents, not replacing the variable itself.
Convert a Pydantic model to a dictionary
The model_dump() method converts a Pydantic model into a plain Python dictionary:
expense.model_dump()
# {"description": "Lunch", "amount": 12.5, "category": "food", "date": None}This gives you a regular dictionary you can modify freely. The Pydantic model's job is done after validation.
Add the identifier with the spread operator
The ** spread operator unpacks a dictionary into key-value pairs. You can use it to create a new dictionary that combines existing fields with new ones:
{"id": counter, **expense.model_dump()}
# {"id": 1, "description": "Lunch", "amount": 12.5, "category": "food", "date": None}This creates a single dictionary with the id field first, followed by all the expense fields. Storing this in expenses[counter] lets you look up any expense by its identifier later.
Instructions
Fill in the create_expense function body to assign an identifier, store the expense, and return it.
- Add
global counterto access the module-level counter. - Increment
counterby 1. - Store the expense in the dictionary with
expenses[counter] = {"id": counter, **expense.model_dump()}. - Return
expenses[counter].
from datetime import datetime
from fastapi import FastAPI
from pydantic import BaseModel, Field, field_validator
from typing import Literal, Optional
app = FastAPI()
class Expense(BaseModel):
description: str = Field(min_length=1)
amount: float = Field(gt=0)
category: Literal["food", "transport", "entertainment", "utilities", "other"]
date: Optional[str] = None
@field_validator("date")
@classmethod
def validate_date_format(cls, v):
if v is not None:
try:
datetime.strptime(v, "%Y-%m-%d")
except ValueError:
raise ValueError("Date must be in YYYY-MM-DD format")
return v
expenses = {}
counter = 0
@app.post("/expenses", status_code=201)
def create_expense(expense: Expense):
# Step 1: Add global counter
# Step 2: Increment counter by 1
# Step 3: Store expenses[counter] = {"id": counter, **expense.model_dump()}
# Step 4: Return expenses[counter]
Interactive Code Editor
Sign in to write and run code, track your progress, and unlock all chapters.
Sign In to Start Coding