We have been using the same `Hero` model to declare the schema of the data we receive in the API, the table model in the database, and the schema of the data we send back in responses.
And we want the `id` to be generated automatically by the database, so we don't want the client to send it.
We'll see how to fix it in a bit.
## Review Response Schema
Now let's review the schema of the response we send back to the client in the docs UI.
If you click the small tab <kbd>Schema</kbd> instead of the <kbd>Example Value</kbd>, you will see something like this:
<imgclass="shadow"alt="Interactive API docs UI"src="/img/tutorial/fastapi/multiple-models/image01.png">
Let's see the details.
The fields with a red asterisk (<spanstyle="color: #ff0000;">*</span>) are "required".
This means that our API application is required to return those fields in the response:
*`name`
*`secret_name`
The `age` is optional, we don't have to return it, or it could be `None` (or `null` in JSON), but the `name` and the `secret_name` are required.
Here's the weird thing, the `id` currently seems also "optional". 🤔
This is because in our **SQLModel** class we declare the `id` with `Optional[int]`, because it could be `None` in memory until we save it in the database and we finally get the actual ID.
But in the responses, we would always send a model from the database, and it would **always have an ID**. So the `id` in the responses could be declared as required too.
This would mean that our application is making the compromise with the clients that if it sends a hero, it would for sure have an `id` with a value, it would not be `None`.
### Why Is it Important to Compromise with the Responses
The ultimate goal of an API is for some **clients to use it**.
The clients could be a frontend application, a command line program, a graphical user interface, a mobile application, another backend application, etc.
Making both sides very clear will make it much easier to interact with the API.
And in most of the cases, the developer of the client for that API **will also be yourself**, so you are **doing your future self a favor** by declaring those schemas for requests and responses. 😉
### So Why is it Important to Have Required IDs
Now, what's the matter with having one **`id` field marked as "optional"** in a response when in reality it is always required?
For example, **automatically generated clients** in other languages (or also in Python) would have some declaration that this field `id` is optional.
And then the developers using those clients in their languages would have to be checking all the time in all their code if the `id` is not `None` before using it anywhere.
That's a lot of unnecessary checks and **unnecessary code** that could have been saved by declaring the schema properly. 😔
It would be a lot simpler for that code to know that the `id` from a response is required and **will always have a value**.
Let's fix that too. 🤓
## Multiple Hero Schemas
So, we want to have our `Hero` model that declares the **data in the database**:
*`id`, optional on creation, required on database
*`name`, required
*`secret_name`, required
*`age`, optional
But we also want to have a `HeroCreate` for the data we want to receive when **creating** a new hero, which is almost all the same data as `Hero`, except for the `id`, because that is created automatically by the database:
*`name`, required
*`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:
*`id`, required
*`name`, required
*`secret_name`, required
*`age`, optional
## Multiple Models with Duplicated Fields
The simplest way to solve it could be to create **multiple models**, each one with all the corresponding fields:
```Python hl_lines="5-9 12-15 18-22"
# This would work, but there's a better option below 🚨
Here's the important detail, and probably the most important feature of **SQLModel**: only `Hero` is declared with `table = True`.
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).
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. 🚀
!!! tip
We will improve this code to avoid duplicating the fields, but for now we can continue learning with these models.
## Use Multiple Models to Create a Hero
Let's now see how to use these new models in the FastAPI application.
Let's first check how is the process to create a hero now:
But as in this case, we have a `HeroCreate` instance in the `hero` variable. This is an object with attributes, so we use `.model_validate()` to read those attributes.
With this, we create a new `Hero` instance (the one for the database) and put it in the variable `db_hero` from the data in the `hero` variable that is the `HeroCreate` instance we received from the request.
Then we just `add` it to the **session**, `commit`, and `refresh` it, and finally, we return the same `db_hero` variable that has the just refreshed `Hero` instance.
This filtering could be very important and could be a very good security feature, for example, to make sure you filter private data, hashed passwords, etc.
You can read more about it in the <ahref="https://fastapi.tiangolo.com/tutorial/response-model/"class="external-link"target="_blank">FastAPI docs about Response Model</a>.
This is important if, for example, in the future, we decide to **refactor the code** and rename one field (column). For example, from `secret_name` to `secret_identity`.
If we have that duplicated in multiple models, we could easily forget to update one of them. But if we **avoid duplication**, there's only one place that would need updating. ✨
Let's now improve that. 🤓
## Multiple Models with Inheritance
And here it is, you found the biggest feature of **SQLModel**. 💎
Each of these models is only a **data model** or both a data model and a **table model**.
So, it's possible to create models with **SQLModel** that don't represent tables in the database.
On top of that, we can use inheritance to avoid duplicated information in these models.
We can see from above that they all share some **base** fields:
*`name`, required
*`secret_name`, required
*`age`, optional
So let's create a **base** model `HeroBase` that the others can inherit from:
This won't affect this parent **data model**`HeroBase`.
But once the child model `Hero` (the actual **table model**) inherits those fields, it will use those field configurations to create the indexes when creating the tables in the database.
As an alternative, we could use `HeroBase` directly in the API code instead of `HeroCreate`, but it would show up in the automatic docs UI with that name "`HeroBase`" which could be **confusing** for clients. Instead, "`HeroCreate`" is a bit more explicit about what it is for.
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.
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.
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.
This is a very simple example, and it might look a bit... meh. 😅
But now imagine that your table has **10 or 20 columns**. And that you have to duplicate all that information for all your **data models**... then it becomes more obvious why it's quite useful to be able to avoid all that information duplication with inheritance.
Now, this probably looks so flexible that it's not obvious **when to use inheritance** and for what.
Here are a couple of rules of thumb that can help you.
### Only Inherit from Data Models
Only inherit from **data models**, don't inherit from **table models**.
It will help you avoid confusion, and there won't be any reason for you to need to inherit from a **table model**.
If you feel like you need to inherit from a **table model**, then instead create a **base** class that is only a **data model** and has all those fields, like `HeroBase`.
And then inherit from that **base** class that is only a **data model** for any other **data model** and for the **table model**.
### Avoid Duplication - Keep it Simple
It could feel like you need to have a profound reason why to inherit from one model or another, because "in some mystical way" they separate different concepts... or something like that.
In some cases, there are **simple separations** that you can use, like the models to create data, read, update, etc. If that's quick and obvious, nice, use it. 💯
Otherwise, don't worry too much about profound conceptual reasons to separate models, just try to **avoid duplication** and **keep the code simple** enough to reason about it.
If you see you have a lot of **overlap** between two models, then you can probably **avoid some of that duplication** with a base model.
But if to avoid some duplication you end up with a crazy tree of models with inheritance, then it might be **simpler** to just duplicate some of those fields, and that might be easier to reason about and to maintain.
Do whatever is easier to **reason** about, to **program** with, to **maintain**, and to **refactor** in the future. 🤓
Remember that inheritance, the same as **SQLModel**, and anything else, are just tools to **help you be more productive**, that's one of their main objectives. If something is not helping with that (e.g. too much duplication, too much complexity), then change it. 🚀
## Recap
You can use **SQLModel** to declare multiple models:
* Some models can be only **data models**. They will also be **Pydantic** models.
* And some can *also* be **table models** (apart from already being **data models**) by having the config `table = True`. They will also be **Pydantic** models and **SQLAlchemy** models.
Only the **table models** will create tables in the database.
So, you can use all the other **data models** to validate, convert, filter, and document the schema of the data for your application. ✨
You can use inheritance to **avoid information and code duplication**. 😎
And you can use all these models directly with **FastAPI**. 🚀