FastAPI - 간단한 백엔드 구현하기

홍찬우·2023년 7월 30일

Errors

Error loading ASGI app. Import string "app" must be in format ":"

  • main에서 uvicorn.run("app”) 이 아닌, uvicorn.run("main:app”) 형태로 해야 함

    • "main: app"에서 main은 main 코드 파일 이름

fastapi.exceptions.FastAPIError: Invalid args for response field!

  • 에러나는 코드
async def return_prediction(request: Request,
                        input_text: str = Form(...),
                        model: T5ForConditionalGeneration = get_model(),
                        tokenizer: T5TokenizerFast = get_tokenizer()
                        ):
  • 에러 해결 코드
async def return_prediction(request: Request,
                        input_text: str = Form(...),
                        model: T5ForConditionalGeneration = Depends(get_model),
                        tokenizer: T5TokenizerFast = Depends(get_tokenizer),
                        ):

왜 Depends로 의존성을 주입하니 해결이 되는가?

↓↓↓↓↓↓↓↓↓↓

Dependency

Depends

  • 의존성 주입이란?

    • 똑같은 로직을 비효율적으로 여러 번 사용하는 것을 방지하고자 사용
  • dependency가 걸린 함수는 데코레이터가 없는 path operation 이라고 생각


Class Dependency

  • 클래스를 dependency로 사용

Sub Dependency

  • dependency의 dependency를 원하는 만큼 깊게 만들 수 있음
def query_extractor(q: Optional[str] = None):
    return q

def query_or_cookie_extractor(
    q: str = Depends(query_extractor), last_query: Optional[str] = Cookie(None)
):
    if not q:
        return last_query
    return q

@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}
  • read_query() 함수는 query_or_cookie_extractor() 를 dependency로 가지며, query_or_cookie_extractor() 함수는 query_extractor() 함수를 dependency로 가짐

Code

from typing import Optional
from fastapi import Depends, FastAPI

app = FastAPI()

async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}

@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons

@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    return commons
  • read_items, read_users 모두 common parameters를 dependency로 사용하므로 두 함수의 리턴 값은 같음

class CommonQueryParams:
    def __init__(self, q: Optional[str] = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit

@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
  • 위에서 사용한 common_parameters() 함수를 다음과 같이 class로 변경해서 사용 가능

commons: CommonQueryParams = Depends(CommonQueryParams)
commons = Depends(CommonQueryParams)
commons: CommonQueryParams = Depends()
  • 위 세 코드는 모두 같은 코드이며, Depends() 처럼 파라미터를 넘겨주지 않으면 commons의 명시된 type으로 인스턴스를 만들어 리턴

알게 된 것

form 형식으로 값을 받을 때, html에서 name과 fastAPI에서 변수명은 일치해야 함

<form method="post">
      <textarea name="input_text" rows="20" cols="100"></textarea>
      <input type="submit" value="요약">
  </form>
@app.post('/')
def return_prediction(request: Request,
                        input_text: str = Form(...)
                      ):
	  summary = predict(input_text)
    return template.TemplateResponse('front.html', context={"request": request, "summary": summary})
  • textarea name을 input_text로 지정했으므로, 함수에서 변수도 input_text로 사용해야 함

  • 현재는 요약문이 완성되면 다시 html을 뿌려주는 방식


BaseModel

  • 왜 사용하는가?

    1. 데이터 모델링

      • 데이터의 자료형을 지정해준 타입으로 제한
    2. 유효성 검사

      • 입력받은 데이터의 자료형이 맞지 않으면 validation error를 raise

전체 코드

FastAPI (main.py)

from fastapi import FastAPI, Form, Request
from fastapi.templating import Jinja2Templates
import uvicorn
from pydantic import BaseModel
from model import get_model, get_tokenizer, predict
from transformers import T5TokenizerFast, T5ForConditionalGeneration
from fastapi.param_functions import Depends

app = FastAPI()
template = Jinja2Templates(directory='./')

class Text(BaseModel):
    text: str

@app.get('/')
def get_html(request: Request):
    return template.TemplateResponse('front.html', context={"request": request})

@app.post('/', response_model=Text)
async def return_prediction(request: Request,
                        input_text: str = Form(...),
                        model: T5ForConditionalGeneration = Depends(get_model),
                        tokenizer: T5TokenizerFast = Depends(get_tokenizer),
                        ):

    summary = predict(input_text, model, tokenizer)
    return template.TemplateResponse('front.html', context={"request": request, "summary": summary}) 

if __name__ == "__main__":
    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
  • Text BaseModel을 사용해 return

  • 근데 return은 TemplateResponse인데 왜 response_model=Text 을 해도 에러가 안 나는가?

    return Text(text=template.TemplateResponse('front.html',
    context={"request": request, "summary": summary}))

    return을 위와 같이 변경하니 type match가 안 된다며 error가 뜸

  • chatgpt 피셜

  • JSON 직렬화(serialization) 이란, JSON으로 변환 가능한 객체를 의미


HTML (front.html)

<!DOCTYPE html>
<html>
<head>
    <title>텍스트 요약</title>
</head>
<body>
    <h1>텍스트 요약</h1>
  
    <form method="post">
        <textarea name="input_text" rows="20" cols="100"></textarea>
        <input type="submit" value="요약">
    </form>

    <br>
    <textarea rows="20" cols="50">{{summary}}</textarea>
</body>
</html>
profile
AI-Kid

0개의 댓글