# Unit 17: Additional Models

## Introduction

In this unit, we will implement a demo program that includes many Pydantic models for specific use-cases:

* The **input model** needs to be able to have a password.
* The **output model** should not have a password.
* The **database model** would probably need to store a hashed password.

***

## Example program

{% code lineNumbers="true" %}

```python
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()

class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str = None

class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str = None

class UserDB(BaseModel):
    username: str
    hashed_password: str
    email: EmailStr
    full_name: str = None

def fake_password_hasher(raw_password: str):
    """This is a fake password hasher, don't use it in production."""
    return "salted_" + raw_password

def fake_save_user(user_in: UserIn):
    hashed_password = fake_password_hasher(user_in.password)
    user_db = UserDB(**user_in.model_dump(), hashed_password=hashed_password)
    print("User has been saved to an imaginary database!")
    return user_db

@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
    user_saved = fake_save_user(user)
    return user_saved
```

{% endcode %}

{% hint style="info" %}
Within function definitions, the double asterisk, `**`, is used in conjunction with the parameter name `**kwargs` (though `kwargs` itself is not mandatory). This allows you to accept a variable number of keyword arguments when the function is called.
{% endhint %}

### Reduce code duplication

In the above program, there are code snippets duplicated across class definition. As code duplication increases the chances of bugs, security issues, code desynchronization issues (when you update in one place but not in the others), etc.

{% code lineNumbers="true" %}

```python
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()

class UserBase(BaseModel):
    username: str
    email: EmailStr
    full_name: str = None

class UserIn(UserBase):
    password: str

class UserOut(UserBase):
    pass

class UserDB(UserBase):
    hashed_password: str

def fake_password_hasher(raw_password: str):
    """This is a fake password hasher, don't use it in production."""
    return "salted_" + raw_password

def fake_save_user(user_in: UserIn):
    hashed_password = fake_password_hasher(user_in.password)
    user_db = UserDB(**user_in.model_dump(), hashed_password=hashed_password)
    print("User has been saved to an imaginary database!")
    return user_db

@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
    user_saved = fake_save_user(user)
    return user_saved
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://fastapi-docs.duonghuuphuc.com/unit-17-additional-models.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
