Cheatsheet for @computed_field.

Basic

from pydantic import BaseModel, computed_field

class User(BaseModel):
    first: str
    last: str
    
    @computed_field
    @property
    def full_name(self) -> str:
        return f"{self.first} {self.last}"

user = User(first="Alice", last="X")
user.model_dump()
# {"first": "Alice", "last": "X", "full_name": "Alice X"}

@computed_field decorates @property. Type annotation required.

Alias

@computed_field(alias="fullName")
@property
def full_name(self) -> str:
    return f"{self.first} {self.last}"

user.model_dump(by_alias=True)
# {"first": "...", "last": "...", "fullName": "..."}

Description / examples

@computed_field(
    description="Concatenated first + last name",
    examples=["Alice X"],
)
@property
def full_name(self) -> str:
    return f"{self.first} {self.last}"

Exclude from output

@computed_field
@property
def expensive(self) -> int:
    return self._compute()

user.model_dump(exclude={"expensive"})

Or return_type arg:

@computed_field(repr=False, return_type=str)
@property
def hidden_in_repr(self) -> str:
    return "secret"

Cached vs not

By default, @computed_field re-runs on each access. For caching:

from functools import cached_property

class User(BaseModel):
    first: str
    last: str
    
    @computed_field
    @cached_property
    def full_name(self) -> str:
        return f"{self.first} {self.last}"
    
    model_config = {"ignored_types": (cached_property,)}   # don't validate property

cached_property runs once; subsequent accesses return cached.

In JSON Schema

Appears as a read-only property. validation schema doesn’t include it (not accepted on input); serialization schema does.

User.model_json_schema(mode="validation")     # no full_name
User.model_json_schema(mode="serialization")  # includes full_name

Use cases

Derived attributes

class Order(BaseModel):
    quantity: int
    unit_price: Decimal
    
    @computed_field
    @property
    def total(self) -> Decimal:
        return self.quantity * self.unit_price

Counts

class User(BaseModel):
    posts: list[Post]
    
    @computed_field
    @property
    def post_count(self) -> int:
        return len(self.posts)

Status from state

class Job(BaseModel):
    started_at: datetime
    completed_at: datetime | None
    
    @computed_field
    @property
    def status(self) -> str:
        return "completed" if self.completed_at else "running"

Public-facing API field

class UserOut(BaseModel):
    id: int
    first: str
    last: str
    
    @computed_field
    @property
    def display_name(self) -> str:
        return f"{self.first} {self.last}".strip() or f"user{self.id}"

Not for input

@computed_field is output-only. Don’t expect to set it from input:

User(first="Alice", last="X", full_name="Custom")    # error or ignored

If you need input + derived: use plain field + model_validator(mode="after") to compute.

In FastAPI

class UserOut(BaseModel):
    id: int
    first: str
    last: str
    
    @computed_field
    @property
    def full_name(self) -> str:
        return f"{self.first} {self.last}"

@app.get("/users/{id}", response_model=UserOut)
async def get_user(id: int): ...

full_name appears in OpenAPI response schema.

With SQLAlchemy hybrid_property

# SA model
class User(Base):
    first: Mapped[str]
    last: Mapped[str]
    
    @hybrid_property
    def full_name(self) -> str:
        return f"{self.first} {self.last}"
    
    @full_name.expression
    @classmethod
    def full_name(cls):
        return func.concat(cls.first, " ", cls.last)

# Pydantic
class UserOut(BaseModel):
    id: int
    full_name: str          # populated from SA hybrid_property
    
    model_config = {"from_attributes": True}

Read attribute from SA via getattr. Don’t need @computed_field if SA has hybrid_property.

Common mistakes

  • Missing return type annotation — @computed_field requires it.
  • Trying to accept input — it’s serialization-only.
  • Returning non-JSON-serializable types in mode="json" — set a serializer.
  • Heavy computation per access — use cached_property or compute in model_validator(mode="after").

Read this next

If you want my computed-field patterns for API responses, it’s at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .