ConfigDict & from_attributes

RoadmapsPython Backend

Scenario

You query the database for a user and return it directly from a FastAPI endpoint using a `response_model`. The database query succeeds, but the client gets a `500 Internal Server Error` complaining that `value is not a valid dict`.

How do you tell Pydantic that the data isn't a dictionary, but an object with attributes?

Mental model

It's like a translator that knows both languages. By default, Pydantic only speaks 'Dictionary' (`data['id']`). `from_attributes` teaches it to also speak 'Object' (`data.id`).

SQLAlchemy returns objects where data is accessed via dot-notation (e.g., `user.email`). By default, Pydantic tries to access data using bracket-notation (e.g., `user['email']`). `model_config = ConfigDict(from_attributes=True)` flips a switch inside Pydantic, telling it: 'If it's not a dictionary, try looking for attributes with the same name before you give up and throw an error.'

Deep dive

When you build a backend, you typically have two types of models: the ORM model (SQLAlchemy) that represents the database table, and the Pydantic model that represents the API response. You query the database to get an ORM object, and you want FastAPI to return it as JSON.

ORM models map to the DB; Pydantic models map to the API.

If you just pass the ORM object to a standard Pydantic model, it crashes. Pydantic expects a dictionary by default. It tries to do `orm_object['id']`, which raises a `TypeError` because ORM models use attributes (`orm_object.id`), not keys.

Pydantic expects dictionaries; SQLAlchemy returns objects.

In Pydantic v2, the fix is adding `model_config = ConfigDict(from_attributes=True)` to your response schema. This tells Pydantic to fall back to checking `getattr(obj, 'field_name')` if dictionary lookup fails. Now it seamlessly translates the ORM model into the JSON response through serialization.

`from_attributes=True` tells Pydantic to read object attributes, bridging the gap.

Be careful with legacy code. In Pydantic v1, this was done using an inner class: `class Config: orm_mode = True`. Pydantic v2 replaced this entirely with `ConfigDict(from_attributes=True)`. Using the v1 idiom today means your code won't run, and in an interview, it signals you haven't written modern Python in years.

Pydantic v2 uses `ConfigDict(from_attributes=True)`; v1 used `class Config: orm_mode = True`.

Code examples

The broken way (default Pydantic behavior)

from pydantic import BaseModel

class UserResponse(BaseModel):
    id: int
    name: str

# Imagine this is a SQLAlchemy object returned from the DB
class DBUser:
    def __init__(self, id, name):
        self.id = id
        self.name = name

db_user = DBUser(id=1, name="Alice")

# This crashes! Pydantic tries to do db_user['id']
response = UserResponse(**db_user)  # TypeError

Standard Pydantic models only understand dictionaries. They don't know how to read the attributes of an object like `db_user.id`.

The modern fix (Pydantic v2)

from pydantic import BaseModel, ConfigDict

class UserResponse(BaseModel):
    # This is the Pydantic v2 way to read from ORM objects
    model_config = ConfigDict(from_attributes=True)
    
    id: int
    name: str

db_user = DBUser(id=1, name="Alice")

# Now this works perfectly using .model_validate()
# FastAPI does this automatically under the hood when using response_model
response = UserResponse.model_validate(db_user)
print(response.model_dump_json())  # {"id":1,"name":"Alice"}

`ConfigDict(from_attributes=True)` tells Pydantic to look for `db_user.id` when it builds the response. Always use `model_validate()` (v2) instead of `from_orm()` (v1) to instantiate it from an object.

The rejected answer (Pydantic v1)

from pydantic import BaseModel

class UserResponse(BaseModel):
    id: int
    name: str

    # DON'T DO THIS IN 2026
    class Config:
        orm_mode = True  # Deprecated and removed in Pydantic v2

This was how you did it in Pydantic v1. If you write this in an interview now, it shows you haven't kept up with the modern ecosystem, as Pydantic v2 has been the standard for years.

Key points

Common mistakes

Glossary

ORM model
An Object-Relational Mapping object (like in SQLAlchemy) where data is stored and accessed as class attributes rather than dictionary keys.
serialization
The process of converting complex data structures like Python objects into a format like JSON that can be sent over a network.

Recall questions

Questions & answers

Your FastAPI endpoint queries a `User` from the database and returns it, with `response_model=UserResponse`. The query succeeds, but the endpoint throws a 500 error: 'value is not a valid dict'. What is the issue?

The `UserResponse` Pydantic model is trying to read the SQLAlchemy `User` object as a dictionary. To fix this, add `model_config = ConfigDict(from_attributes=True)` to the `UserResponse` model so it reads the object's attributes instead.

Approach: Recognize the classic mismatch between ORM objects and Pydantic dict-parsing. Specify the modern Pydantic v2 fix.

A junior developer writes `class Config: orm_mode = True` in a new FastAPI project. Why should this be flagged in code review?

It is the obsolete Pydantic v1 syntax. Pydantic v2 replaced it with `model_config = ConfigDict(from_attributes=True)`. The v1 syntax will be ignored or cause errors in modern environments.

Approach: Identify the outdated idiom immediately and provide the exact Pydantic v2 replacement.

Continue learning

Previous: Pydantic Models and Validators

Previous: Async SQLAlchemy and SQLModel

Return to Python Backend Roadmap