Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modifieddocs/img/tutorial/fastapi/multiple-models/image03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions docs/tutorial/fastapi/multiple-models.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,7 +98,7 @@ But we also want to have a `HeroCreate` for the data we want to receive when **c
* `secret_name`, required
* `age`, optional

And we want to have a `HeroRead` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:
And we want to have a `HeroPublic` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:

* `id`, required
* `name`, required
Expand DownExpand Up@@ -183,9 +183,9 @@ Here's the important detail, and probably the most important feature of **SQLMod

This means that the class `Hero` represents a **table** in the database. It is both a **Pydantic** model and a **SQLAlchemy** model.

But `HeroCreate` and `HeroRead` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).
But `HeroCreate` and `HeroPublic` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).

This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroRead`, because they don't have `table = True`, which is exactly what we want. 🚀
This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroPublic`, because they don't have `table = True`, which is exactly what we want. 🚀

/// tip

Expand DownExpand Up@@ -355,7 +355,7 @@ Then we just `add` it to the **session**, `commit`, and `refresh` it, and finall

Because it is just refreshed, it has the `id` field set with a new ID taken from the database.

And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroRead`:
And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroPublic`:

//// tab | Python 3.10+

Expand DownExpand Up@@ -743,9 +743,9 @@ As an alternative, we could use `HeroBase` directly in the API code instead of `

On top of that, we could easily decide in the future that we want to receive **more data** when creating a new hero apart from the data in `HeroBase` (for example, a password), and now we already have the class to put those extra fields.

### The `HeroRead` **Data Model**
### The `HeroPublic` **Data Model**

Now let's check the `HeroRead` model.
Now let's check the `HeroPublic` model.

This one just declares that the `id` field is required when reading a hero from the API, because a hero read from the API will come from the database, and in the database it will always have an ID.

Expand DownExpand Up@@ -815,7 +815,7 @@ This one just declares that the `id` field is required when reading a hero from

## Review the Updated Docs UI

The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroRead`. But now, we define them in a smarter way with inheritance.
The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroPublic`. But now, we define them in a smarter way with inheritance.

So, we can jump to the docs UI right away and see how they look with the updated data.

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/read-one.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,7 +164,7 @@ This will let the client know that they probably made a mistake on their side an

Then, if the hero exists, we return it.

And because we are using the `response_model` with `HeroRead`, it will be validated, documented, etc.
And because we are using the `response_model` with `HeroPublic`, it will be validated, documented, etc.

//// tab | Python 3.10+

Expand Down
20 changes: 10 additions & 10 deletions docs/tutorial/fastapi/relationships.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,9 +40,9 @@ Let's update that. 🤓

First, why is it that we are not getting the related data for each hero and for each team?

It's because we declared the `HeroRead` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.
It's because we declared the `HeroPublic` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.

And the same way, we declared the `TeamRead` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.
And the same way, we declared the `TeamPublic` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.

//// tab | Python 3.10+

Expand DownExpand Up@@ -146,7 +146,7 @@ And the same way, we declared the `TeamRead` with only the same base fields of t

Now, remember that <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI uses the `response_model` to validate and **filter** the response data</a>?

In this case, we used `response_model=TeamRead` and `response_model=HeroRead`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:
In this case, we used `response_model=TeamPublic` and `response_model=HeroPublic`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:

//// tab | Python 3.10+

Expand DownExpand Up@@ -300,7 +300,7 @@ Let's add a couple more **data models** that declare that data so we can use the

## Models with Relationships

Let's add the models `HeroReadWithTeam` and `TeamReadWithHeroes`.
Let's add the models `HeroPublicWithTeam` and `TeamPublicWithHeroes`.

We'll add them **after** the other models so that we can easily reference the previous models.

Expand DownExpand Up@@ -372,11 +372,11 @@ These two models are very **simple in code**, but there's a lot happening here.

### Inheritance and Type Annotations

The `HeroReadWithTeam` **inherits** from `HeroRead`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroRead`.
The `HeroPublicWithTeam` **inherits** from `HeroPublic`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroPublic`.

And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamRead` with the base fields for reading a team.
And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamPublic` with the base fields for reading a team.

Then we do the same for the `TeamReadWithHeroes`, it **inherits** from `TeamRead`, and declares the **new field** `heroes`, which is a list of `HeroRead`.
Then we do the same for the `TeamPublicWithHeroes`, it **inherits** from `TeamPublic`, and declares the **new field** `heroes`, which is a list of `HeroPublic`.

### Data Models Without Relationship Attributes

Expand All@@ -386,11 +386,11 @@ Instead, here these are only **data models** that will tell FastAPI **which attr

### Reference to Other Models

Also, notice that the field `team` is not declared with this new `TeamReadWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamRead` model.
Also, notice that the field `team` is not declared with this new `TeamPublicWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamPublic` model.

And the same for `TeamReadWithHeroes`, the model used for the new field `heroes` uses `HeroRead` to get only each hero's data.
And the same for `TeamPublicWithHeroes`, the model used for the new field `heroes` uses `HeroPublic` to get only each hero's data.

This also means that, even though we have these two new models, **we still need the previous ones**, `HeroRead` and `TeamRead`, because we need to reference them here (and we are also using them in the rest of the *path operations*).
This also means that, even though we have these two new models, **we still need the previous ones**, `HeroPublic` and `TeamPublic`, because we need to reference them here (and we are also using them in the rest of the *path operations*).

## Update the Path Operations

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/teams.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,7 @@ It's the same process we did for heroes, with a base model, a **table model**, a

We have a `TeamBase` **data model**, and from it, we inherit with a `Team` **table model**.

Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamRead` **data models**.
Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamPublic` **data models**.

And we also create a `TeamUpdate` **data model**.

Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -50,7 +50,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -59,7 +59,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -70,15 +70,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py310.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -45,7 +45,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -55,14 +55,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -71,7 +71,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py39.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modifieddocs/img/tutorial/fastapi/multiple-models/image03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions docs/tutorial/fastapi/multiple-models.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,7 +98,7 @@ But we also want to have a `HeroCreate` for the data we want to receive when **c
* `secret_name`, required
* `age`, optional

And we want to have a `HeroRead` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:
And we want to have a `HeroPublic` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:

* `id`, required
* `name`, required
Expand DownExpand Up@@ -183,9 +183,9 @@ Here's the important detail, and probably the most important feature of **SQLMod

This means that the class `Hero` represents a **table** in the database. It is both a **Pydantic** model and a **SQLAlchemy** model.

But `HeroCreate` and `HeroRead` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).
But `HeroCreate` and `HeroPublic` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).

This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroRead`, because they don't have `table = True`, which is exactly what we want. 🚀
This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroPublic`, because they don't have `table = True`, which is exactly what we want. 🚀

/// tip

Expand DownExpand Up@@ -355,7 +355,7 @@ Then we just `add` it to the **session**, `commit`, and `refresh` it, and finall

Because it is just refreshed, it has the `id` field set with a new ID taken from the database.

And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroRead`:
And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroPublic`:

//// tab | Python 3.10+

Expand DownExpand Up@@ -743,9 +743,9 @@ As an alternative, we could use `HeroBase` directly in the API code instead of `

On top of that, we could easily decide in the future that we want to receive **more data** when creating a new hero apart from the data in `HeroBase` (for example, a password), and now we already have the class to put those extra fields.

### The `HeroRead` **Data Model**
### The `HeroPublic` **Data Model**

Now let's check the `HeroRead` model.
Now let's check the `HeroPublic` model.

This one just declares that the `id` field is required when reading a hero from the API, because a hero read from the API will come from the database, and in the database it will always have an ID.

Expand DownExpand Up@@ -815,7 +815,7 @@ This one just declares that the `id` field is required when reading a hero from

## Review the Updated Docs UI

The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroRead`. But now, we define them in a smarter way with inheritance.
The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroPublic`. But now, we define them in a smarter way with inheritance.

So, we can jump to the docs UI right away and see how they look with the updated data.

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/read-one.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,7 +164,7 @@ This will let the client know that they probably made a mistake on their side an

Then, if the hero exists, we return it.

And because we are using the `response_model` with `HeroRead`, it will be validated, documented, etc.
And because we are using the `response_model` with `HeroPublic`, it will be validated, documented, etc.

//// tab | Python 3.10+

Expand Down
20 changes: 10 additions & 10 deletions docs/tutorial/fastapi/relationships.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,9 +40,9 @@ Let's update that. 🤓

First, why is it that we are not getting the related data for each hero and for each team?

It's because we declared the `HeroRead` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.
It's because we declared the `HeroPublic` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.

And the same way, we declared the `TeamRead` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.
And the same way, we declared the `TeamPublic` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.

//// tab | Python 3.10+

Expand DownExpand Up@@ -146,7 +146,7 @@ And the same way, we declared the `TeamRead` with only the same base fields of t

Now, remember that <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI uses the `response_model` to validate and **filter** the response data</a>?

In this case, we used `response_model=TeamRead` and `response_model=HeroRead`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:
In this case, we used `response_model=TeamPublic` and `response_model=HeroPublic`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:

//// tab | Python 3.10+

Expand DownExpand Up@@ -300,7 +300,7 @@ Let's add a couple more **data models** that declare that data so we can use the

## Models with Relationships

Let's add the models `HeroReadWithTeam` and `TeamReadWithHeroes`.
Let's add the models `HeroPublicWithTeam` and `TeamPublicWithHeroes`.

We'll add them **after** the other models so that we can easily reference the previous models.

Expand DownExpand Up@@ -372,11 +372,11 @@ These two models are very **simple in code**, but there's a lot happening here.

### Inheritance and Type Annotations

The `HeroReadWithTeam` **inherits** from `HeroRead`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroRead`.
The `HeroPublicWithTeam` **inherits** from `HeroPublic`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroPublic`.

And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamRead` with the base fields for reading a team.
And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamPublic` with the base fields for reading a team.

Then we do the same for the `TeamReadWithHeroes`, it **inherits** from `TeamRead`, and declares the **new field** `heroes`, which is a list of `HeroRead`.
Then we do the same for the `TeamPublicWithHeroes`, it **inherits** from `TeamPublic`, and declares the **new field** `heroes`, which is a list of `HeroPublic`.

### Data Models Without Relationship Attributes

Expand All@@ -386,11 +386,11 @@ Instead, here these are only **data models** that will tell FastAPI **which attr

### Reference to Other Models

Also, notice that the field `team` is not declared with this new `TeamReadWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamRead` model.
Also, notice that the field `team` is not declared with this new `TeamPublicWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamPublic` model.

And the same for `TeamReadWithHeroes`, the model used for the new field `heroes` uses `HeroRead` to get only each hero's data.
And the same for `TeamPublicWithHeroes`, the model used for the new field `heroes` uses `HeroPublic` to get only each hero's data.

This also means that, even though we have these two new models, **we still need the previous ones**, `HeroRead` and `TeamRead`, because we need to reference them here (and we are also using them in the rest of the *path operations*).
This also means that, even though we have these two new models, **we still need the previous ones**, `HeroPublic` and `TeamPublic`, because we need to reference them here (and we are also using them in the rest of the *path operations*).

## Update the Path Operations

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/teams.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,7 @@ It's the same process we did for heroes, with a base model, a **table model**, a

We have a `TeamBase` **data model**, and from it, we inherit with a `Team` **table model**.

Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamRead` **data models**.
Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamPublic` **data models**.

And we also create a `TeamUpdate` **data model**.

Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -50,7 +50,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -59,7 +59,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -70,15 +70,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py310.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -45,7 +45,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -55,14 +55,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -71,7 +71,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py39.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modifieddocs/img/tutorial/fastapi/multiple-models/image03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions docs/tutorial/fastapi/multiple-models.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,7 +98,7 @@ But we also want to have a `HeroCreate` for the data we want to receive when **c
* `secret_name`, required
* `age`, optional

And we want to have a `HeroRead` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:
And we want to have a `HeroPublic` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:

* `id`, required
* `name`, required
Expand DownExpand Up@@ -183,9 +183,9 @@ Here's the important detail, and probably the most important feature of **SQLMod

This means that the class `Hero` represents a **table** in the database. It is both a **Pydantic** model and a **SQLAlchemy** model.

But `HeroCreate` and `HeroRead` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).
But `HeroCreate` and `HeroPublic` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).

This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroRead`, because they don't have `table = True`, which is exactly what we want. 🚀
This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroPublic`, because they don't have `table = True`, which is exactly what we want. 🚀

/// tip

Expand DownExpand Up@@ -355,7 +355,7 @@ Then we just `add` it to the **session**, `commit`, and `refresh` it, and finall

Because it is just refreshed, it has the `id` field set with a new ID taken from the database.

And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroRead`:
And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroPublic`:

//// tab | Python 3.10+

Expand DownExpand Up@@ -743,9 +743,9 @@ As an alternative, we could use `HeroBase` directly in the API code instead of `

On top of that, we could easily decide in the future that we want to receive **more data** when creating a new hero apart from the data in `HeroBase` (for example, a password), and now we already have the class to put those extra fields.

### The `HeroRead` **Data Model**
### The `HeroPublic` **Data Model**

Now let's check the `HeroRead` model.
Now let's check the `HeroPublic` model.

This one just declares that the `id` field is required when reading a hero from the API, because a hero read from the API will come from the database, and in the database it will always have an ID.

Expand DownExpand Up@@ -815,7 +815,7 @@ This one just declares that the `id` field is required when reading a hero from

## Review the Updated Docs UI

The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroRead`. But now, we define them in a smarter way with inheritance.
The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroPublic`. But now, we define them in a smarter way with inheritance.

So, we can jump to the docs UI right away and see how they look with the updated data.

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/read-one.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,7 +164,7 @@ This will let the client know that they probably made a mistake on their side an

Then, if the hero exists, we return it.

And because we are using the `response_model` with `HeroRead`, it will be validated, documented, etc.
And because we are using the `response_model` with `HeroPublic`, it will be validated, documented, etc.

//// tab | Python 3.10+

Expand Down
20 changes: 10 additions & 10 deletions docs/tutorial/fastapi/relationships.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,9 +40,9 @@ Let's update that. 🤓

First, why is it that we are not getting the related data for each hero and for each team?

It's because we declared the `HeroRead` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.
It's because we declared the `HeroPublic` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.

And the same way, we declared the `TeamRead` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.
And the same way, we declared the `TeamPublic` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.

//// tab | Python 3.10+

Expand DownExpand Up@@ -146,7 +146,7 @@ And the same way, we declared the `TeamRead` with only the same base fields of t

Now, remember that <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI uses the `response_model` to validate and **filter** the response data</a>?

In this case, we used `response_model=TeamRead` and `response_model=HeroRead`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:
In this case, we used `response_model=TeamPublic` and `response_model=HeroPublic`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:

//// tab | Python 3.10+

Expand DownExpand Up@@ -300,7 +300,7 @@ Let's add a couple more **data models** that declare that data so we can use the

## Models with Relationships

Let's add the models `HeroReadWithTeam` and `TeamReadWithHeroes`.
Let's add the models `HeroPublicWithTeam` and `TeamPublicWithHeroes`.

We'll add them **after** the other models so that we can easily reference the previous models.

Expand DownExpand Up@@ -372,11 +372,11 @@ These two models are very **simple in code**, but there's a lot happening here.

### Inheritance and Type Annotations

The `HeroReadWithTeam` **inherits** from `HeroRead`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroRead`.
The `HeroPublicWithTeam` **inherits** from `HeroPublic`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroPublic`.

And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamRead` with the base fields for reading a team.
And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamPublic` with the base fields for reading a team.

Then we do the same for the `TeamReadWithHeroes`, it **inherits** from `TeamRead`, and declares the **new field** `heroes`, which is a list of `HeroRead`.
Then we do the same for the `TeamPublicWithHeroes`, it **inherits** from `TeamPublic`, and declares the **new field** `heroes`, which is a list of `HeroPublic`.

### Data Models Without Relationship Attributes

Expand All@@ -386,11 +386,11 @@ Instead, here these are only **data models** that will tell FastAPI **which attr

### Reference to Other Models

Also, notice that the field `team` is not declared with this new `TeamReadWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamRead` model.
Also, notice that the field `team` is not declared with this new `TeamPublicWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamPublic` model.

And the same for `TeamReadWithHeroes`, the model used for the new field `heroes` uses `HeroRead` to get only each hero's data.
And the same for `TeamPublicWithHeroes`, the model used for the new field `heroes` uses `HeroPublic` to get only each hero's data.

This also means that, even though we have these two new models, **we still need the previous ones**, `HeroRead` and `TeamRead`, because we need to reference them here (and we are also using them in the rest of the *path operations*).
This also means that, even though we have these two new models, **we still need the previous ones**, `HeroPublic` and `TeamPublic`, because we need to reference them here (and we are also using them in the rest of the *path operations*).

## Update the Path Operations

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/teams.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,7 @@ It's the same process we did for heroes, with a base model, a **table model**, a

We have a `TeamBase` **data model**, and from it, we inherit with a `Team` **table model**.

Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamRead` **data models**.
Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamPublic` **data models**.

And we also create a `TeamUpdate` **data model**.

Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -50,7 +50,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -59,7 +59,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -70,15 +70,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py310.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -45,7 +45,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -55,14 +55,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -71,7 +71,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py39.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modifieddocs/img/tutorial/fastapi/multiple-models/image03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions docs/tutorial/fastapi/multiple-models.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,7 +98,7 @@ But we also want to have a `HeroCreate` for the data we want to receive when **c
* `secret_name`, required
* `age`, optional

And we want to have a `HeroRead` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:
And we want to have a `HeroPublic` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:

* `id`, required
* `name`, required
Expand DownExpand Up@@ -183,9 +183,9 @@ Here's the important detail, and probably the most important feature of **SQLMod

This means that the class `Hero` represents a **table** in the database. It is both a **Pydantic** model and a **SQLAlchemy** model.

But `HeroCreate` and `HeroRead` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).
But `HeroCreate` and `HeroPublic` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).

This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroRead`, because they don't have `table = True`, which is exactly what we want. 🚀
This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroPublic`, because they don't have `table = True`, which is exactly what we want. 🚀

/// tip

Expand DownExpand Up@@ -355,7 +355,7 @@ Then we just `add` it to the **session**, `commit`, and `refresh` it, and finall

Because it is just refreshed, it has the `id` field set with a new ID taken from the database.

And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroRead`:
And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroPublic`:

//// tab | Python 3.10+

Expand DownExpand Up@@ -743,9 +743,9 @@ As an alternative, we could use `HeroBase` directly in the API code instead of `

On top of that, we could easily decide in the future that we want to receive **more data** when creating a new hero apart from the data in `HeroBase` (for example, a password), and now we already have the class to put those extra fields.

### The `HeroRead` **Data Model**
### The `HeroPublic` **Data Model**

Now let's check the `HeroRead` model.
Now let's check the `HeroPublic` model.

This one just declares that the `id` field is required when reading a hero from the API, because a hero read from the API will come from the database, and in the database it will always have an ID.

Expand DownExpand Up@@ -815,7 +815,7 @@ This one just declares that the `id` field is required when reading a hero from

## Review the Updated Docs UI

The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroRead`. But now, we define them in a smarter way with inheritance.
The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroPublic`. But now, we define them in a smarter way with inheritance.

So, we can jump to the docs UI right away and see how they look with the updated data.

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/read-one.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,7 +164,7 @@ This will let the client know that they probably made a mistake on their side an

Then, if the hero exists, we return it.

And because we are using the `response_model` with `HeroRead`, it will be validated, documented, etc.
And because we are using the `response_model` with `HeroPublic`, it will be validated, documented, etc.

//// tab | Python 3.10+

Expand Down
20 changes: 10 additions & 10 deletions docs/tutorial/fastapi/relationships.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,9 +40,9 @@ Let's update that. 🤓

First, why is it that we are not getting the related data for each hero and for each team?

It's because we declared the `HeroRead` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.
It's because we declared the `HeroPublic` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.

And the same way, we declared the `TeamRead` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.
And the same way, we declared the `TeamPublic` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.

//// tab | Python 3.10+

Expand DownExpand Up@@ -146,7 +146,7 @@ And the same way, we declared the `TeamRead` with only the same base fields of t

Now, remember that <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI uses the `response_model` to validate and **filter** the response data</a>?

In this case, we used `response_model=TeamRead` and `response_model=HeroRead`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:
In this case, we used `response_model=TeamPublic` and `response_model=HeroPublic`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:

//// tab | Python 3.10+

Expand DownExpand Up@@ -300,7 +300,7 @@ Let's add a couple more **data models** that declare that data so we can use the

## Models with Relationships

Let's add the models `HeroReadWithTeam` and `TeamReadWithHeroes`.
Let's add the models `HeroPublicWithTeam` and `TeamPublicWithHeroes`.

We'll add them **after** the other models so that we can easily reference the previous models.

Expand DownExpand Up@@ -372,11 +372,11 @@ These two models are very **simple in code**, but there's a lot happening here.

### Inheritance and Type Annotations

The `HeroReadWithTeam` **inherits** from `HeroRead`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroRead`.
The `HeroPublicWithTeam` **inherits** from `HeroPublic`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroPublic`.

And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamRead` with the base fields for reading a team.
And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamPublic` with the base fields for reading a team.

Then we do the same for the `TeamReadWithHeroes`, it **inherits** from `TeamRead`, and declares the **new field** `heroes`, which is a list of `HeroRead`.
Then we do the same for the `TeamPublicWithHeroes`, it **inherits** from `TeamPublic`, and declares the **new field** `heroes`, which is a list of `HeroPublic`.

### Data Models Without Relationship Attributes

Expand All@@ -386,11 +386,11 @@ Instead, here these are only **data models** that will tell FastAPI **which attr

### Reference to Other Models

Also, notice that the field `team` is not declared with this new `TeamReadWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamRead` model.
Also, notice that the field `team` is not declared with this new `TeamPublicWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamPublic` model.

And the same for `TeamReadWithHeroes`, the model used for the new field `heroes` uses `HeroRead` to get only each hero's data.
And the same for `TeamPublicWithHeroes`, the model used for the new field `heroes` uses `HeroPublic` to get only each hero's data.

This also means that, even though we have these two new models, **we still need the previous ones**, `HeroRead` and `TeamRead`, because we need to reference them here (and we are also using them in the rest of the *path operations*).
This also means that, even though we have these two new models, **we still need the previous ones**, `HeroPublic` and `TeamPublic`, because we need to reference them here (and we are also using them in the rest of the *path operations*).

## Update the Path Operations

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/teams.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,7 @@ It's the same process we did for heroes, with a base model, a **table model**, a

We have a `TeamBase` **data model**, and from it, we inherit with a `Team` **table model**.

Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamRead` **data models**.
Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamPublic` **data models**.

And we also create a `TeamUpdate` **data model**.

Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -50,7 +50,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -59,7 +59,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -70,15 +70,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py310.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -45,7 +45,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -55,14 +55,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -71,7 +71,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py39.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modifieddocs/img/tutorial/fastapi/multiple-models/image03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions docs/tutorial/fastapi/multiple-models.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,7 +98,7 @@ But we also want to have a `HeroCreate` for the data we want to receive when **c
* `secret_name`, required
* `age`, optional

And we want to have a `HeroRead` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:
And we want to have a `HeroPublic` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:

* `id`, required
* `name`, required
Expand DownExpand Up@@ -183,9 +183,9 @@ Here's the important detail, and probably the most important feature of **SQLMod

This means that the class `Hero` represents a **table** in the database. It is both a **Pydantic** model and a **SQLAlchemy** model.

But `HeroCreate` and `HeroRead` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).
But `HeroCreate` and `HeroPublic` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).

This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroRead`, because they don't have `table = True`, which is exactly what we want. 🚀
This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroPublic`, because they don't have `table = True`, which is exactly what we want. 🚀

/// tip

Expand DownExpand Up@@ -355,7 +355,7 @@ Then we just `add` it to the **session**, `commit`, and `refresh` it, and finall

Because it is just refreshed, it has the `id` field set with a new ID taken from the database.

And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroRead`:
And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroPublic`:

//// tab | Python 3.10+

Expand DownExpand Up@@ -743,9 +743,9 @@ As an alternative, we could use `HeroBase` directly in the API code instead of `

On top of that, we could easily decide in the future that we want to receive **more data** when creating a new hero apart from the data in `HeroBase` (for example, a password), and now we already have the class to put those extra fields.

### The `HeroRead` **Data Model**
### The `HeroPublic` **Data Model**

Now let's check the `HeroRead` model.
Now let's check the `HeroPublic` model.

This one just declares that the `id` field is required when reading a hero from the API, because a hero read from the API will come from the database, and in the database it will always have an ID.

Expand DownExpand Up@@ -815,7 +815,7 @@ This one just declares that the `id` field is required when reading a hero from

## Review the Updated Docs UI

The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroRead`. But now, we define them in a smarter way with inheritance.
The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroPublic`. But now, we define them in a smarter way with inheritance.

So, we can jump to the docs UI right away and see how they look with the updated data.

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/read-one.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,7 +164,7 @@ This will let the client know that they probably made a mistake on their side an

Then, if the hero exists, we return it.

And because we are using the `response_model` with `HeroRead`, it will be validated, documented, etc.
And because we are using the `response_model` with `HeroPublic`, it will be validated, documented, etc.

//// tab | Python 3.10+

Expand Down
20 changes: 10 additions & 10 deletions docs/tutorial/fastapi/relationships.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,9 +40,9 @@ Let's update that. 🤓

First, why is it that we are not getting the related data for each hero and for each team?

It's because we declared the `HeroRead` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.
It's because we declared the `HeroPublic` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.

And the same way, we declared the `TeamRead` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.
And the same way, we declared the `TeamPublic` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.

//// tab | Python 3.10+

Expand DownExpand Up@@ -146,7 +146,7 @@ And the same way, we declared the `TeamRead` with only the same base fields of t

Now, remember that <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI uses the `response_model` to validate and **filter** the response data</a>?

In this case, we used `response_model=TeamRead` and `response_model=HeroRead`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:
In this case, we used `response_model=TeamPublic` and `response_model=HeroPublic`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:

//// tab | Python 3.10+

Expand DownExpand Up@@ -300,7 +300,7 @@ Let's add a couple more **data models** that declare that data so we can use the

## Models with Relationships

Let's add the models `HeroReadWithTeam` and `TeamReadWithHeroes`.
Let's add the models `HeroPublicWithTeam` and `TeamPublicWithHeroes`.

We'll add them **after** the other models so that we can easily reference the previous models.

Expand DownExpand Up@@ -372,11 +372,11 @@ These two models are very **simple in code**, but there's a lot happening here.

### Inheritance and Type Annotations

The `HeroReadWithTeam` **inherits** from `HeroRead`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroRead`.
The `HeroPublicWithTeam` **inherits** from `HeroPublic`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroPublic`.

And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamRead` with the base fields for reading a team.
And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamPublic` with the base fields for reading a team.

Then we do the same for the `TeamReadWithHeroes`, it **inherits** from `TeamRead`, and declares the **new field** `heroes`, which is a list of `HeroRead`.
Then we do the same for the `TeamPublicWithHeroes`, it **inherits** from `TeamPublic`, and declares the **new field** `heroes`, which is a list of `HeroPublic`.

### Data Models Without Relationship Attributes

Expand All@@ -386,11 +386,11 @@ Instead, here these are only **data models** that will tell FastAPI **which attr

### Reference to Other Models

Also, notice that the field `team` is not declared with this new `TeamReadWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamRead` model.
Also, notice that the field `team` is not declared with this new `TeamPublicWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamPublic` model.

And the same for `TeamReadWithHeroes`, the model used for the new field `heroes` uses `HeroRead` to get only each hero's data.
And the same for `TeamPublicWithHeroes`, the model used for the new field `heroes` uses `HeroPublic` to get only each hero's data.

This also means that, even though we have these two new models, **we still need the previous ones**, `HeroRead` and `TeamRead`, because we need to reference them here (and we are also using them in the rest of the *path operations*).
This also means that, even though we have these two new models, **we still need the previous ones**, `HeroPublic` and `TeamPublic`, because we need to reference them here (and we are also using them in the rest of the *path operations*).

## Update the Path Operations

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/teams.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,7 @@ It's the same process we did for heroes, with a base model, a **table model**, a

We have a `TeamBase` **data model**, and from it, we inherit with a `Team` **table model**.

Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamRead` **data models**.
Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamPublic` **data models**.

And we also create a `TeamUpdate` **data model**.

Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -50,7 +50,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -59,7 +59,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -70,15 +70,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py310.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -45,7 +45,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -55,14 +55,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -71,7 +71,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py39.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modifieddocs/img/tutorial/fastapi/multiple-models/image03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions docs/tutorial/fastapi/multiple-models.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,7 +98,7 @@ But we also want to have a `HeroCreate` for the data we want to receive when **c
* `secret_name`, required
* `age`, optional

And we want to have a `HeroRead` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:
And we want to have a `HeroPublic` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:

* `id`, required
* `name`, required
Expand DownExpand Up@@ -183,9 +183,9 @@ Here's the important detail, and probably the most important feature of **SQLMod

This means that the class `Hero` represents a **table** in the database. It is both a **Pydantic** model and a **SQLAlchemy** model.

But `HeroCreate` and `HeroRead` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).
But `HeroCreate` and `HeroPublic` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).

This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroRead`, because they don't have `table = True`, which is exactly what we want. 🚀
This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroPublic`, because they don't have `table = True`, which is exactly what we want. 🚀

/// tip

Expand DownExpand Up@@ -355,7 +355,7 @@ Then we just `add` it to the **session**, `commit`, and `refresh` it, and finall

Because it is just refreshed, it has the `id` field set with a new ID taken from the database.

And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroRead`:
And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroPublic`:

//// tab | Python 3.10+

Expand DownExpand Up@@ -743,9 +743,9 @@ As an alternative, we could use `HeroBase` directly in the API code instead of `

On top of that, we could easily decide in the future that we want to receive **more data** when creating a new hero apart from the data in `HeroBase` (for example, a password), and now we already have the class to put those extra fields.

### The `HeroRead` **Data Model**
### The `HeroPublic` **Data Model**

Now let's check the `HeroRead` model.
Now let's check the `HeroPublic` model.

This one just declares that the `id` field is required when reading a hero from the API, because a hero read from the API will come from the database, and in the database it will always have an ID.

Expand DownExpand Up@@ -815,7 +815,7 @@ This one just declares that the `id` field is required when reading a hero from

## Review the Updated Docs UI

The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroRead`. But now, we define them in a smarter way with inheritance.
The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroPublic`. But now, we define them in a smarter way with inheritance.

So, we can jump to the docs UI right away and see how they look with the updated data.

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/read-one.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,7 +164,7 @@ This will let the client know that they probably made a mistake on their side an

Then, if the hero exists, we return it.

And because we are using the `response_model` with `HeroRead`, it will be validated, documented, etc.
And because we are using the `response_model` with `HeroPublic`, it will be validated, documented, etc.

//// tab | Python 3.10+

Expand Down
20 changes: 10 additions & 10 deletions docs/tutorial/fastapi/relationships.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,9 +40,9 @@ Let's update that. 🤓

First, why is it that we are not getting the related data for each hero and for each team?

It's because we declared the `HeroRead` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.
It's because we declared the `HeroPublic` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.

And the same way, we declared the `TeamRead` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.
And the same way, we declared the `TeamPublic` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.

//// tab | Python 3.10+

Expand DownExpand Up@@ -146,7 +146,7 @@ And the same way, we declared the `TeamRead` with only the same base fields of t

Now, remember that <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI uses the `response_model` to validate and **filter** the response data</a>?

In this case, we used `response_model=TeamRead` and `response_model=HeroRead`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:
In this case, we used `response_model=TeamPublic` and `response_model=HeroPublic`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:

//// tab | Python 3.10+

Expand DownExpand Up@@ -300,7 +300,7 @@ Let's add a couple more **data models** that declare that data so we can use the

## Models with Relationships

Let's add the models `HeroReadWithTeam` and `TeamReadWithHeroes`.
Let's add the models `HeroPublicWithTeam` and `TeamPublicWithHeroes`.

We'll add them **after** the other models so that we can easily reference the previous models.

Expand DownExpand Up@@ -372,11 +372,11 @@ These two models are very **simple in code**, but there's a lot happening here.

### Inheritance and Type Annotations

The `HeroReadWithTeam` **inherits** from `HeroRead`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroRead`.
The `HeroPublicWithTeam` **inherits** from `HeroPublic`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroPublic`.

And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamRead` with the base fields for reading a team.
And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamPublic` with the base fields for reading a team.

Then we do the same for the `TeamReadWithHeroes`, it **inherits** from `TeamRead`, and declares the **new field** `heroes`, which is a list of `HeroRead`.
Then we do the same for the `TeamPublicWithHeroes`, it **inherits** from `TeamPublic`, and declares the **new field** `heroes`, which is a list of `HeroPublic`.

### Data Models Without Relationship Attributes

Expand All@@ -386,11 +386,11 @@ Instead, here these are only **data models** that will tell FastAPI **which attr

### Reference to Other Models

Also, notice that the field `team` is not declared with this new `TeamReadWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamRead` model.
Also, notice that the field `team` is not declared with this new `TeamPublicWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamPublic` model.

And the same for `TeamReadWithHeroes`, the model used for the new field `heroes` uses `HeroRead` to get only each hero's data.
And the same for `TeamPublicWithHeroes`, the model used for the new field `heroes` uses `HeroPublic` to get only each hero's data.

This also means that, even though we have these two new models, **we still need the previous ones**, `HeroRead` and `TeamRead`, because we need to reference them here (and we are also using them in the rest of the *path operations*).
This also means that, even though we have these two new models, **we still need the previous ones**, `HeroPublic` and `TeamPublic`, because we need to reference them here (and we are also using them in the rest of the *path operations*).

## Update the Path Operations

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/teams.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,7 @@ It's the same process we did for heroes, with a base model, a **table model**, a

We have a `TeamBase` **data model**, and from it, we inherit with a `Team` **table model**.

Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamRead` **data models**.
Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamPublic` **data models**.

And we also create a `TeamUpdate` **data model**.

Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -50,7 +50,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -59,7 +59,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -70,15 +70,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py310.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -45,7 +45,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -55,14 +55,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -71,7 +71,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py39.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modifieddocs/img/tutorial/fastapi/multiple-models/image03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions docs/tutorial/fastapi/multiple-models.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,7 +98,7 @@ But we also want to have a `HeroCreate` for the data we want to receive when **c
* `secret_name`, required
* `age`, optional

And we want to have a `HeroRead` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:
And we want to have a `HeroPublic` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:

* `id`, required
* `name`, required
Expand DownExpand Up@@ -183,9 +183,9 @@ Here's the important detail, and probably the most important feature of **SQLMod

This means that the class `Hero` represents a **table** in the database. It is both a **Pydantic** model and a **SQLAlchemy** model.

But `HeroCreate` and `HeroRead` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).
But `HeroCreate` and `HeroPublic` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).

This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroRead`, because they don't have `table = True`, which is exactly what we want. 🚀
This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroPublic`, because they don't have `table = True`, which is exactly what we want. 🚀

/// tip

Expand DownExpand Up@@ -355,7 +355,7 @@ Then we just `add` it to the **session**, `commit`, and `refresh` it, and finall

Because it is just refreshed, it has the `id` field set with a new ID taken from the database.

And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroRead`:
And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroPublic`:

//// tab | Python 3.10+

Expand DownExpand Up@@ -743,9 +743,9 @@ As an alternative, we could use `HeroBase` directly in the API code instead of `

On top of that, we could easily decide in the future that we want to receive **more data** when creating a new hero apart from the data in `HeroBase` (for example, a password), and now we already have the class to put those extra fields.

### The `HeroRead` **Data Model**
### The `HeroPublic` **Data Model**

Now let's check the `HeroRead` model.
Now let's check the `HeroPublic` model.

This one just declares that the `id` field is required when reading a hero from the API, because a hero read from the API will come from the database, and in the database it will always have an ID.

Expand DownExpand Up@@ -815,7 +815,7 @@ This one just declares that the `id` field is required when reading a hero from

## Review the Updated Docs UI

The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroRead`. But now, we define them in a smarter way with inheritance.
The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroPublic`. But now, we define them in a smarter way with inheritance.

So, we can jump to the docs UI right away and see how they look with the updated data.

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/read-one.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,7 +164,7 @@ This will let the client know that they probably made a mistake on their side an

Then, if the hero exists, we return it.

And because we are using the `response_model` with `HeroRead`, it will be validated, documented, etc.
And because we are using the `response_model` with `HeroPublic`, it will be validated, documented, etc.

//// tab | Python 3.10+

Expand Down
20 changes: 10 additions & 10 deletions docs/tutorial/fastapi/relationships.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,9 +40,9 @@ Let's update that. 🤓

First, why is it that we are not getting the related data for each hero and for each team?

It's because we declared the `HeroRead` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.
It's because we declared the `HeroPublic` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.

And the same way, we declared the `TeamRead` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.
And the same way, we declared the `TeamPublic` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.

//// tab | Python 3.10+

Expand DownExpand Up@@ -146,7 +146,7 @@ And the same way, we declared the `TeamRead` with only the same base fields of t

Now, remember that <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI uses the `response_model` to validate and **filter** the response data</a>?

In this case, we used `response_model=TeamRead` and `response_model=HeroRead`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:
In this case, we used `response_model=TeamPublic` and `response_model=HeroPublic`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:

//// tab | Python 3.10+

Expand DownExpand Up@@ -300,7 +300,7 @@ Let's add a couple more **data models** that declare that data so we can use the

## Models with Relationships

Let's add the models `HeroReadWithTeam` and `TeamReadWithHeroes`.
Let's add the models `HeroPublicWithTeam` and `TeamPublicWithHeroes`.

We'll add them **after** the other models so that we can easily reference the previous models.

Expand DownExpand Up@@ -372,11 +372,11 @@ These two models are very **simple in code**, but there's a lot happening here.

### Inheritance and Type Annotations

The `HeroReadWithTeam` **inherits** from `HeroRead`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroRead`.
The `HeroPublicWithTeam` **inherits** from `HeroPublic`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroPublic`.

And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamRead` with the base fields for reading a team.
And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamPublic` with the base fields for reading a team.

Then we do the same for the `TeamReadWithHeroes`, it **inherits** from `TeamRead`, and declares the **new field** `heroes`, which is a list of `HeroRead`.
Then we do the same for the `TeamPublicWithHeroes`, it **inherits** from `TeamPublic`, and declares the **new field** `heroes`, which is a list of `HeroPublic`.

### Data Models Without Relationship Attributes

Expand All@@ -386,11 +386,11 @@ Instead, here these are only **data models** that will tell FastAPI **which attr

### Reference to Other Models

Also, notice that the field `team` is not declared with this new `TeamReadWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamRead` model.
Also, notice that the field `team` is not declared with this new `TeamPublicWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamPublic` model.

And the same for `TeamReadWithHeroes`, the model used for the new field `heroes` uses `HeroRead` to get only each hero's data.
And the same for `TeamPublicWithHeroes`, the model used for the new field `heroes` uses `HeroPublic` to get only each hero's data.

This also means that, even though we have these two new models, **we still need the previous ones**, `HeroRead` and `TeamRead`, because we need to reference them here (and we are also using them in the rest of the *path operations*).
This also means that, even though we have these two new models, **we still need the previous ones**, `HeroPublic` and `TeamPublic`, because we need to reference them here (and we are also using them in the rest of the *path operations*).

## Update the Path Operations

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/teams.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,7 @@ It's the same process we did for heroes, with a base model, a **table model**, a

We have a `TeamBase` **data model**, and from it, we inherit with a `Team` **table model**.

Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamRead` **data models**.
Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamPublic` **data models**.

And we also create a `TeamUpdate` **data model**.

Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -50,7 +50,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -59,7 +59,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -70,15 +70,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py310.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -45,7 +45,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -55,14 +55,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -71,7 +71,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py39.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modifieddocs/img/tutorial/fastapi/multiple-models/image03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions docs/tutorial/fastapi/multiple-models.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,7 +98,7 @@ But we also want to have a `HeroCreate` for the data we want to receive when **c
* `secret_name`, required
* `age`, optional

And we want to have a `HeroRead` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:
And we want to have a `HeroPublic` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:

* `id`, required
* `name`, required
Expand DownExpand Up@@ -183,9 +183,9 @@ Here's the important detail, and probably the most important feature of **SQLMod

This means that the class `Hero` represents a **table** in the database. It is both a **Pydantic** model and a **SQLAlchemy** model.

But `HeroCreate` and `HeroRead` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).
But `HeroCreate` and `HeroPublic` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).

This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroRead`, because they don't have `table = True`, which is exactly what we want. 🚀
This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroPublic`, because they don't have `table = True`, which is exactly what we want. 🚀

/// tip

Expand DownExpand Up@@ -355,7 +355,7 @@ Then we just `add` it to the **session**, `commit`, and `refresh` it, and finall

Because it is just refreshed, it has the `id` field set with a new ID taken from the database.

And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroRead`:
And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroPublic`:

//// tab | Python 3.10+

Expand DownExpand Up@@ -743,9 +743,9 @@ As an alternative, we could use `HeroBase` directly in the API code instead of `

On top of that, we could easily decide in the future that we want to receive **more data** when creating a new hero apart from the data in `HeroBase` (for example, a password), and now we already have the class to put those extra fields.

### The `HeroRead` **Data Model**
### The `HeroPublic` **Data Model**

Now let's check the `HeroRead` model.
Now let's check the `HeroPublic` model.

This one just declares that the `id` field is required when reading a hero from the API, because a hero read from the API will come from the database, and in the database it will always have an ID.

Expand DownExpand Up@@ -815,7 +815,7 @@ This one just declares that the `id` field is required when reading a hero from

## Review the Updated Docs UI

The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroRead`. But now, we define them in a smarter way with inheritance.
The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroPublic`. But now, we define them in a smarter way with inheritance.

So, we can jump to the docs UI right away and see how they look with the updated data.

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/read-one.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,7 +164,7 @@ This will let the client know that they probably made a mistake on their side an

Then, if the hero exists, we return it.

And because we are using the `response_model` with `HeroRead`, it will be validated, documented, etc.
And because we are using the `response_model` with `HeroPublic`, it will be validated, documented, etc.

//// tab | Python 3.10+

Expand Down
20 changes: 10 additions & 10 deletions docs/tutorial/fastapi/relationships.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,9 +40,9 @@ Let's update that. 🤓

First, why is it that we are not getting the related data for each hero and for each team?

It's because we declared the `HeroRead` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.
It's because we declared the `HeroPublic` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.

And the same way, we declared the `TeamRead` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.
And the same way, we declared the `TeamPublic` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.

//// tab | Python 3.10+

Expand DownExpand Up@@ -146,7 +146,7 @@ And the same way, we declared the `TeamRead` with only the same base fields of t

Now, remember that <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI uses the `response_model` to validate and **filter** the response data</a>?

In this case, we used `response_model=TeamRead` and `response_model=HeroRead`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:
In this case, we used `response_model=TeamPublic` and `response_model=HeroPublic`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:

//// tab | Python 3.10+

Expand DownExpand Up@@ -300,7 +300,7 @@ Let's add a couple more **data models** that declare that data so we can use the

## Models with Relationships

Let's add the models `HeroReadWithTeam` and `TeamReadWithHeroes`.
Let's add the models `HeroPublicWithTeam` and `TeamPublicWithHeroes`.

We'll add them **after** the other models so that we can easily reference the previous models.

Expand DownExpand Up@@ -372,11 +372,11 @@ These two models are very **simple in code**, but there's a lot happening here.

### Inheritance and Type Annotations

The `HeroReadWithTeam` **inherits** from `HeroRead`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroRead`.
The `HeroPublicWithTeam` **inherits** from `HeroPublic`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroPublic`.

And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamRead` with the base fields for reading a team.
And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamPublic` with the base fields for reading a team.

Then we do the same for the `TeamReadWithHeroes`, it **inherits** from `TeamRead`, and declares the **new field** `heroes`, which is a list of `HeroRead`.
Then we do the same for the `TeamPublicWithHeroes`, it **inherits** from `TeamPublic`, and declares the **new field** `heroes`, which is a list of `HeroPublic`.

### Data Models Without Relationship Attributes

Expand All@@ -386,11 +386,11 @@ Instead, here these are only **data models** that will tell FastAPI **which attr

### Reference to Other Models

Also, notice that the field `team` is not declared with this new `TeamReadWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamRead` model.
Also, notice that the field `team` is not declared with this new `TeamPublicWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamPublic` model.

And the same for `TeamReadWithHeroes`, the model used for the new field `heroes` uses `HeroRead` to get only each hero's data.
And the same for `TeamPublicWithHeroes`, the model used for the new field `heroes` uses `HeroPublic` to get only each hero's data.

This also means that, even though we have these two new models, **we still need the previous ones**, `HeroRead` and `TeamRead`, because we need to reference them here (and we are also using them in the rest of the *path operations*).
This also means that, even though we have these two new models, **we still need the previous ones**, `HeroPublic` and `TeamPublic`, because we need to reference them here (and we are also using them in the rest of the *path operations*).

## Update the Path Operations

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/teams.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,7 @@ It's the same process we did for heroes, with a base model, a **table model**, a

We have a `TeamBase` **data model**, and from it, we inherit with a `Team` **table model**.

Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamRead` **data models**.
Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamPublic` **data models**.

And we also create a `TeamUpdate` **data model**.

Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -50,7 +50,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -59,7 +59,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -70,15 +70,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -52,7 +52,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
Expand All@@ -61,7 +61,7 @@ def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
*,
session: Session = Depends(get_session),
Expand All@@ -72,15 +72,15 @@ def read_heroes(
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(*, session: Session = Depends(get_session), hero_id: int):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
*, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate
):
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=List[HeroRead])
@app.get("/heroes/", response_model=List[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py310.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -45,7 +45,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -55,14 +55,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -71,7 +71,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
10 changes: 5 additions & 5 deletions docs_src/tutorial/fastapi/delete/tutorial001_py39.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


class HeroRead(HeroBase):
class HeroPublic(HeroBase):
id: int


Expand DownExpand Up@@ -47,7 +47,7 @@ def on_startup():
create_db_and_tables()


@app.post("/heroes/", response_model=HeroRead)
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate):
with Session(engine) as session:
db_hero = Hero.model_validate(hero)
Expand All@@ -57,14 +57,14 @@ def create_hero(hero: HeroCreate):
return db_hero


@app.get("/heroes/", response_model=list[HeroRead])
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)):
with Session(engine) as session:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes


@app.get("/heroes/{hero_id}", response_model=HeroRead)
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int):
with Session(engine) as session:
hero = session.get(Hero, hero_id)
Expand All@@ -73,7 +73,7 @@ def read_hero(hero_id: int):
return hero


@app.patch("/heroes/{hero_id}", response_model=HeroRead)
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate):
with Session(engine) as session:
db_hero = session.get(Hero, hero_id)
Expand Down
Loading