작성
·
969
·
수정됨
0
from typing import List
from fastapi import FastAPI, Body, HTTPException, Depends
from pydantic import BaseModel
from sqlalchemy.orm import Session
from database.orm import ToDo
from database.connection import get_db
from database.repository import get_todos
from schema.response import ListToDoResponse, ToDoSchema
app = FastAPI()
@app.get("/")
def heath_check_handler():
return {"ping": "pong"}
todo_data = {
1: {
"id": 1,
"contents": "실전! FastAPI 섹션 0 수강",
"is_done": True,
},
2: {
"id": 2,
"contents": "실전! FastAPI 섹션 1 수강",
"is_done": False,
},
3: {
"id": 3,
"contents": "실전! FastAPI 섹션 2 수강",
"is_done": False,
},
}
@app.get("/todos", status_code=200)
def get_todos_handler(
order: str | None = None,
session: Session = Depends(get_db),
) -> ListToDoResponse:
todos: List[ToDo] = get_todos(session=session)
if order and order == "DESC":
return ListToDoResponse(
todos=[ToDoSchema.from_orm(todo) for todo in todos[::-1]]
)
return ListToDoResponse(
todos=[ToDoSchema.from_orm(todo) for todo in todos]
)
@app.get("/todos/{todo_id}", status_code=200)
def get_todo_handler(todo_id: int):
todo = todo_data.get(todo_id)
if todo:
return todo
raise HTTPException(status_code=404, detail="ToDo Not Found")
class CreateToDoRequest(BaseModel):
id: int
contents: str
is_done: bool
@app.post("/todos", status_code=201)
def create_todo_handler(request: CreateToDoRequest):
todo_data[request.id] = request.dict()
return todo_data[request.id]
@app.patch("/todos/{todo_id}", status_code=200)
def update_todo_handler(
todo_id: int,
is_done: bool = Body(..., embed=True)
):
todo = todo_data.get(todo_id)
if todo:
todo["is_done"] = is_done
return todo
raise HTTPException(status_code=404, detail="ToDo Not Found")
@app.delete("/todos/{todo_id}")
def delete_todo_handler(todo_id: int):
todo = todo_data.pop(todo_id, None)
if todo:
return
raise HTTPException(status_code=404, detail="ToDo Not Found")
from typing import List
from pydantic import BaseModel
class ToDoSchema(BaseModel):
id: int
contents: str
is_done: bool
class Config:
orm_mode = True
class ListToDoResponse(BaseModel):
todos: List[ToDoSchema]
from typing import List
from sqlalchemy import select
from sqlalchemy.orm import Session
from database.orm import ToDo
def get_todos(session: Session) -> List[ToDo]:
return list(session.scalars(select(ToDo)))
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "mysql+pymysql://root:todos@127.0.0.1:3306/todos"
engine = create_engine(DATABASE_URL, echo=True)
SessionFactory = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def get_db():
session = SessionFactory()
try:
yield session
finally:
session.close()
from sqlalchemy import Boolean, Column, Integer, String
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class ToDo(Base):
__tablename__ = 'todo'
id = Column(Integer, primary_key=True, index=True)
contents = Column(String(256), nullable=False)
is_done = Column(Boolean, nullable=False)
def __repr__(self):
return f"ToDo(id={self.id}, contents={self.contents}, is_done={self.is_done})"
에러가 납니다.
파이썬 콘솔
<input>:1: PydanticDeprecatedSince20: The from_orm
method is deprecated;
from schema.response import ToDoSchema
from datagbase.orm import ToDo
Traceback (most recent call last):
File "C:\Program Files\JetBrains\PyCharm 2023.3.2\plugins\python\helpers\pydev\pydevconsole.py", line 364, in runcode
coro = func()
^^^^^^
File "<input>", line 1, in <module>
File "C:\Program Files\JetBrains\PyCharm 2023.3.2\plugins\python\helpers\pydev\_pydev_bundle\pydev_import_hook.py", line 21, in do_import
module = self._system_import(name, args, *kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ModuleNotFoundError: No module named 'datagbase'
from database.orm import ToDo
todo = ToDo(id=100, contents="test", is_done=True)
ToDoSchema.from_orm(todo)
<input>:1: PydanticDeprecatedSince20: The from_orm
method is deprecated; set model_config['from_attributes']=True
and use model_validate
instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/
Traceback (most recent call last):
File "C:\Program Files\JetBrains\PyCharm 2023.3.2\plugins\python\helpers\pydev\pydevconsole.py", line 364, in runcode
coro = func()
^^^^^^
File "<input>", line 1, in <module>
File "C:\Users\manag\pyProject\todos\Lib\site-packages\typing_extensions.py", line 2499, in wrapper
return arg(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\Lib\site-packages\pydantic\main.py", line 1126, in from_orm
raise PydanticUserError(
pydantic.errors.PydanticUserError: You must set the config attribute from_attributes=True
to use from_orm
http://localhost:8000/docs#/default/get_todos_handler_todos_get
접속시 터미널
INFO: 127.0.0.1:56312 - "GET /docs HTTP/1.1" 200 OK
INFO: 127.0.0.1:56312 - "GET /openapi.json HTTP/1.1" 200 OK
2024-01-23 16:06:15,108 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2024-01-23 16:06:15,109 INFO sqlalchemy.engine.Engine SELECT todo.id, todo.contents, todo.is_done
FROM todo
2024-01-23 16:06:15,109 INFO sqlalchemy.engine.Engine [cached since 826.5s ago] {}
2024-01-23 16:06:15,113 INFO sqlalchemy.engine.Engine ROLLBACK
INFO: 127.0.0.1:56312 - "GET /todos HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "C:\Users\manag\pyProject\todos\Lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 404, in run_asgi
result = await app( # type: ignore[func-returns-value]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 84, in call
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\Lib\site-packages\fastapi\applications.py", line 1054, in call
await super().__call__(scope, receive, send)
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\applications.py", line 123, in call
await self.middleware_stack(scope, receive, send)
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\middleware\errors.py", line 186, in call
raise exc
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\middleware\errors.py", line 164, in call
await self.app(scope, receive, _send)
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\middleware\exceptions.py", line 62, in call
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\_exception_handler.py", line 64, in wrapped_app
raise exc
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\routing.py", line 762, in call
await self.middleware_stack(scope, receive, send)
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\routing.py", line 782, in app
await route.handle(scope, receive, send)
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\routing.py", line 297, in handle
await self.app(scope, receive, send)
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\routing.py", line 77, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\_exception_handler.py", line 64, in wrapped_app
raise exc
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\routing.py", line 72, in app
response = await func(request)
^^^^^^^^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\Lib\site-packages\fastapi\routing.py", line 299, in app
raise e
File "C:\Users\manag\pyProject\todos\Lib\site-packages\fastapi\routing.py", line 294, in app
raw_response = await run_endpoint_function(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\Lib\site-packages\fastapi\routing.py", line 193, in run_endpoint_function
return await run_in_threadpool(dependant.call, **values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\Lib\site-packages\starlette\concurrency.py", line 40, in run_in_threadpool
return await anyio.to_thread.run_sync(func, *args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\Lib\site-packages\anyio\to_thread.py", line 56, in run_sync
return await get_async_backend().run_sync_in_worker_thread(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\Lib\site-packages\anyio\_backends\_asyncio.py", line 2134, in run_sync_in_worker_thread
return await future
^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\Lib\site-packages\anyio\_backends\_asyncio.py", line 851, in run
result = context.run(func, *args)
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\src\main.py", line 48, in get_todos_handler
todos=[ToDoSchema.from_orm(todo) for todo in todos]
^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\Lib\site-packages\typing_extensions.py", line 2499, in wrapper
return arg(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^
File "C:\Users\manag\pyProject\todos\Lib\site-packages\pydantic\main.py", line 1126, in from_orm
raise PydanticUserError(
pydantic.errors.PydanticUserError: You must set the config attribute from_attributes=True
to use from_orm
답변 2
0
@app.get("/todos/{todo_id}", status_code=200)
def get_todo_handler(todo_id: int, session: Session = Depends(get_db)):
todo = get_todo_by_todo_id(session=session, todo_id=todo_id)
if todo:
return ToDoSchema.model_validate(todo)
raise HTTPException(status_code=404, detail="ToDo Not Found")
컨트롤러는 이렇게 작성하고
from typing import List
from pydantic import BaseModel
class ToDoSchema(BaseModel):
id: int
contents: str
is_done: bool
class Config:
from_attributes = True
class ListToDoResponse(BaseModel):
todos: List[ToDoSchema]
response는 이렇게
from typing import List
from sqlalchemy import select
from sqlalchemy.orm import Session
from database.orm import ToDo
def get_todos(session: Session) -> List[ToDo]:
return list(session.scalars(select(ToDo)))
def get_todo_by_todo_id(session: Session, todo_id: int) -> ToDo | None:
return session.scalar(select(ToDo).where(ToDo.id == todo_id))
repository는 이렇게 작성을 했는데,
제가 원하는건 응답값이
{
todo: {
"id": 1,
"contents": "FastAPI Section 0",
"is_done": true
}
}
이런식으로 래핑을 한건데 저는 결과가
{
"id": 1,
"contents": "FastAPI Section 0",
"is_done": true
}
이렇게 나와요 ㅠ
지금처럼 TodoSchema의 필드가 모두 풀어져서 응답하는게 정상 동작이 맞습니다. 원하시는대로 todo를 key로 갖는 JSON object를 반환하게 하기 위해서는 아래처럼 응답값에 별도로 todo key를 지정해주시거나 별도의 BaseModel을 다시 정의해주셔야 합니다 :)
return {"todo": ToDoSchema.model_validate(todo)}
class TodoResponse(BaseModel):
todo: TodoSchema
0
안녕하세요, 인프런 AI 인턴입니다.
질문자님께서 겪고 계신 문제는 Pydantic 라이브러리의 버전 업데이트에 따라서 from_orm
메서드의 사용이 지원 중단(deprecated)되었기 때문에 발생하는 것으로 보입니다. Pydantic 버전 2.0부터 from_orm
메서드 대신 model_config['from_attributes']=True
를 설정하고 model_validate
를 사용해야 한다는 경고 메시지가 나옵니다.
이를 해결하기 위해서는 Pydantic 모델의 Config
내에 from_attributes=True
설정이 필요하며, 기존의 from_orm
사용 부분을 model_validate
로 변경해야 합니다.
이 문제에 대한 유사한 질문 중 하나가 있으며, 아래 링크에서 확인하실 수 있습니다:
본 질문에서는 pydantic 버전 문제에 대한 답변도 함께 있으니 참고하시면 좋겠습니다. 기존의 from_orm 메서드 사용에서 발생한 문제를 해결하기 위해서는 Pydantic 모델을 업데이트하거나, Pydantic 버전을 downgrade하여 해결이 가능합니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
안녕하세요. from_attributes=True로 설정하고, model_validate(todo)를 사용해서 응답 값을 래핑하려고 했는데, 왜인지 작동하지 않네요. 따로 설정해주어야 하는게 있는건가요?