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