16. Extra Models
1. Extra Models
- 다음과 같은 이유로 사용자 모델의 경우 하나 이상의 관련 모델들이 있는 것이 일반적이다.
1] 입력 모델은 비밀번호를 가질 수 있어야 한다.
2] 출력 모델에는 비밀번호가 없어야 한다.
3] 데이터베이스 모델에는 해시된 비밀번호가 있어야 한다.
2. Multiple models
- 다음의 예제는 모델이 비밀번호를 처리하는 방법에 대한 예제이다.
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: Optional[str] = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: Optional[str] = None
class UserInDB(BaseModel):
username: str
hashed_password: str
email: EmailStr
full_name: Optional[str] = None
def fake_password_hasher(raw_password: str):
return "supersecret" + raw_password
def fake_save_user(user_in: UserIn):
hashed_password = fake_password_hasher(user_in.password)
user_in_db = UserInDB(user_in.dict(), hashed_password=hashed_password)
print("User saved! ..not really")
return user_in_db
@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
user_saved = fake_save_user(user_in)
return user_saved
1) About user_in.dict()
(1) Pydantic's .dict()
user_in
은UserIn
클래스의 Pydantic 모델이다.- Pydantic 모델은 데이터가 있는
dict
를 반환하는.dict()
메소드를 가지고 있다.
- 예를 들어, 다음과 같이 Pydantic 객체인
user_in
을 생성한다고 가정해 보자.
- 위와 같이 생성된
user_in
은.dict()
메소드를 이용하여 다음과 같이 데이터가 있는dict
인 변수user_dict
를 생성할 수 있다.
user_dict
를 다음과 같이 호출할 수 있다.
- 호출된
user_dict
는 Pythondict
의 형태로 다음과 같이 반환된다.
(2) Unwrapping a dict
user_dict
와 같이 언래핑을 통해 다음과 같이user_dict
의 키와 값을 키-값 인수로 직접 전달할 수 있다.
- 위의 코드는 다음의 코드와 같은 것이다.
- 또는 다음의 코드처럼 직접
user_dict
의 항목에 접근하여 전달하는 것과 같은 것이다.
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
(3) A Pydantic model from the contents of another
- 위의 예제에서
user_in.dict()
를 통해user_dict
를 생성했으며, 이를 다음과 같이 언래핑할 수 있다.
- 또는 다음과 같이 언래핑할 수 있다.
- 위의 코드의 결과로 또 다른 Pydantic 모델을 얻게 된다.
(4) Unwrapping a dict
and extra keywords
- 다음과 같이 별도의 키워드 인수
hashed_password=hashed_password
를 추가할 수 있다.
- 위의 코드는 다음과 같은 의미를 가진다.
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
3. Reduce duplication
- 코드의 중복을 줄이는 것은 매우 중요하다.
- 코드 중복으로 인해 버그, 보안 문제, 코드 비동기화 문제 등이 발생할 가능성이 증가한다.
- 또한 많은 데이터를 생성해야 하며, 속성 이름과 타입을 복제해서 사용하게 되므로 복잡해진다.
- 코드 중복을 줄이기 위해 다음과 같이 작성할 수 있다.
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserBase(BaseModel):
username: str
email: EmailStr
full_name: Optional[str] = None
class UserIn(UserBase):
password: str
class UserOut(UserBase):
pass
class UserInDB(UserBase):
hashed_password: str
def fake_password_hasher(raw_password: str):
return "supersecret" + raw_password
def fake_save_user(user_in: UserIn):
hashed_password = fake_password_hasher(user_in.password)
user_in_db = UserInDB(user_in.dict(), hashed_password=hashed_password)
print("User saved! ..not really")
return user_in_db
@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
user_saved = fake_save_user(user_in)
return user_saved
4. Union
or anyOf
- response를 두 가지 타입의
Union
으로 선언할 수 있다. - 즉, response는 둘 중 하나가 된다는 것을 의미한다.
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class BaseItem(BaseModel):
description: str
type: str
class CarItem(BaseItem):
type = "car"
class PlaneItem(BaseItem):
type = "plane"
size: int
items = {
"item1": {"description": "All my friends drive a low rider", "type": "car"},
"item2": {
"description": "Music is my aeroplane, it's my aeroplane",
"type": "plane",
"size": 5,
},
}
@app.get("/items/{item_id}", response_model=Union[PlaneItem, CarItem])
async def read_item(item_id: str):
return items[item_id]
Note
Union
을 정의할 때는 가장 구체적인 타입을 먼저 포함하고, 덜 구체적인 타입을 그 다음에 포함한다.- 위의 예제의
Union[PlaneItem, CarItem]
에서는 더 구체적인PlaneItem
이CarItem
보다 먼저 포함된다.
5. List of models
- 위의 예제와 같은 방법으로 response를 객체들의 리스트로 선언할 수 있다.
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str
items = [
{"name": "Foo", "description": "There comes my hero"},
{"name": "Red", "description": "It's my aeroplane"},
]
@app.get("/items/", response_model=List[Item])
async def read_items():
return items
6. Response with arbitrary dict
- response를 Pydantic 모델을 사용하지 않고 키와 값의 타입만 선언하는 임의의
dict
로 선언할 수 있다. - 이런 방식은 유효한 필드/속성 이름을 모르는 경우에 유용하다.
from typing import Dict
from fastapi import FastAPI
app = FastAPI()
@app.get("/keyword-weights/", response_model=Dict[str, float])
async def read_keyword_weights():
return {"foo": 2.3, "bar": 3.4}