Skip to content

Commit

Permalink
📝 Update ModelRead to ModelPublic documentation and examples (tiangol…
Browse files Browse the repository at this point in the history
…o#885)

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
  • Loading branch information
2 people authored and itsbekas committed Apr 13, 2024
1 parent 74edcd5 commit 07c5d7e
Show file tree
Hide file tree
Showing 68 changed files with 427 additions and 427 deletions.
Binary file modified docs/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 number Diff line number Diff 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 number Diff line number Diff 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 number Diff line number Diff 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 Down Expand 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 Down Expand 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 Down Expand 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 number Diff line number Diff 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 number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


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


Expand Down Expand 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 number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


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


Expand Down Expand 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 number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


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


Expand Down Expand 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 number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


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


Expand Down Expand 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 number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class HeroCreate(HeroBase):
pass


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


Expand Down Expand 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 number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class HeroCreate(HeroBase):
pass


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


Expand Down Expand 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

0 comments on commit 07c5d7e

Please sign in to comment.