Web 14 : FastAPI 연습문제(Car_Information)

haeIT·2024년 8월 22일

Web

목록 보기
13/14
post-thumbnail

2024-08-22 THU

1. Car_Information

  • html, css 파일은 드라이브에서 다운로드

0. 라이브러리(main.py) / 준비

👩‍🎓 라이브러리

from fastapi import FastAPI, Path, HTTPException, status, Body, Request, Form
from fastapi.encoders import jsonable_encoder
from fastapi.responses import RedirectResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, Field
from typing import Optional, List, Dict
from starlette.responses import HTMLResponse
from starlette.status import HTTP_400_BAD_REQUEST 
from database import cars

-1) fastapi

  • 파이썬 웹 프레임워크
  • http 요청 처리, json응답 받음

-2) pydantic

  • 데이터 검증, 직렬화
  • 데이터 모델 정의, json으로 직렬화
  • 직렬화? python객체를 json형식의 문자열로 변환하는 과정

-3) Form

  • HTML 폼 데이터를 처리
  • Form 데이터? 웹서버로 전송되는 데이터. 웹앱이 사용자 입력을 받는 가장 일반적인 방법!
  • ex) 텍스트입력란, 체크박스, 라디오버튼, 드롭다운 목록

-4) Path, HTTPException, status, Body, Request

  • 각각 경로매개변수, 예외처리, 상태코드, 요청본문, 요청객체
  • FastAPI의 구성요소

-5) jsonable_encoder

  • JSON으로 인코딩 가능한 형식으로 데이터 변환

-6) RedirectResponse

  • 다른 URL로 리다이렉션 하는 응답 생성
  • redirection? 웹 브라우저를 다른 url로 자동으로 이동시키는 기술
  • ex) 로그인 화면에서 로그인 성공시 메인페이지로 이동하는것.

-7) Jinja2Template

  • Jinja2 템플릿 엔진을 사용해서 HTML 템플릿을 렌더링

-8) StaticFiles

  • 정적파일 제공
  • ex) CSS, JavaScript, 이미지 등

-9) BaseModel, Field

  • Pydantic라이브러리 일부
  • 데이터 모델과 필드를 제공

-10) Optional, List, Dict

  • 타입힌트

-11) HTMLResponse

  • HTML 콘텐츠를 반환하는 응답 생성

-12) HTTP_400_BAD_REQUEST

  • 400 Bad Request상태코드를 나타내는 상수

-13) database

  • 사용자 정의 모듈
  • 이 코드에선 database.py 파일 안에 cars 객체

👩‍🎓 객체, 모델 준비

templates = Jinja2Templates(directory="templates")

app = FastAPI()

app.mount("/static", StaticFiles(directory="static"), name="static")

class Car(BaseModel):  # Car 모델 정의
    make: Optional[str]
    model: Optional[str]
    year: Optional[int] = Field(..., ge=1970, lt=2022)  # 1970년 이상 2022년 미만의 값만 허용
    price: Optional[float]
    engine: Optional[str] = "V4"
    autonomous: Optional[bool]
    sold: Optional[List[str]]
  • css파일 따로 있어서 app.mount()로 연결
  • ge : greater than
  • lt : less than
  • engine: Optional[str] = "V4" : default값이 "v4"라는 뜻. 입력하지 않으면 기본적으로 v4 할당

1. main.py

  • 기능별로 정리해봄~!

👩‍🎓 목록 조회 기능

@app.get("/", response_class = RedirectResponse)
def read_root(request: Request):
    return RedirectResponse(url="/cars")

@app.get("/cars", response_class = HTMLResponse)
def get_cars(request: Request, number: Optional[str] = Query("10", max_length = 3)):
    response = []
    for id, car in list(cars.items())[:int(number)]:
        response.append((id, car))
    return templates.TemplateResponse("index.html", {"request": request, "cars": response, "title":'Home'})

__ get 엔드포인트 "/cars" 설명

  • get_cars라는 함수는 request와 number라는 매개변수 2개를 받는다
  • 근데 request: Request 현재 http 요청 객체이고,
  • number: Optional[str] = Query~~) 쿼리 파라미터로 기본값은 10, 최대 길이는 3자이다.
  • cars 딕셔너리에서 .items 메서드로 각 자동차 정보를 가져와서 각 자동차의 id:car를 쌍으로 response 리스트에 담는다.
  • index.html을 리턴해야 하니까 TemplateResponse사용
  • 방금 함수에서 수집한 request, response를 json형태로 보낸다.

👩‍🎓 Search 기능

@app.post("/search", response_class = RedirectResponse)
def search_cars(id: str = Form(...)):
    return RedirectResponse(url ="/cars/"+id, status_code=302)

@app.get("/cars/{id}", response_class = HTMLResponse) 
def get_car_by_id(request: Request, id: int = Path(..., ge = 0, lt = 1000)): #greater than, less than
    car = cars.get(id) 
    response = templates.TemplateResponse("search.html", {"request": request, 'car': car, 'title':'Search Car'}) 
    if not car:
        response.status_code = status.HTTP_404_NOT_FOUND 
    return response
  • cars.get(id) : get 메서드는 없는 id 값이 들어와도 오류 발생시키지 않음
  • html 파일(페이지) 리턴 시 request는 항상 같이 리턴해줘야함!
  • 왜? request는 현재 검색한 정보를 담고 있으니까~!
  • if 부분은 car가 없는 경우 404오류 리턴!
  • 왜?? id가 없는 값이 들어와도 get은 오류 발생 안됨. 그러니까 따로 오류인 경우를 정의 해줘야 함!

👩‍🎓 Create 기능

@app.get("/create", response_class=HTMLResponse)
def create_car(request: Request):
    return templates.TemplateResponse("create.html", {"request": request, "title": "Create Car"})

@app.post("/cars", status_code=status.HTTP_201_CREATED)
def add_cars(
    make: Optional[str] = Form(...),
    model: Optional[str] = Form(...),
    year: Optional[str] = Form(...),
    price: Optional[str] = Form(...),
    engine: Optional[str] = Form(...),
    autonomous: Optional[bool] = Form(...),
    sold: Optional[str] = Form(None),
    min_id: Optional[int] = Body(0),
):
    # year 값을 정수로 변환
    try:
        year = int(year) if year is not None else None
    except ValueError:
        raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="Year must be an integer.")

    # sold 값을 리스트로 변환
    if sold is not None:
        sold = [sold] if isinstance(sold, str) else sold #isinstance: sold가 str이면 [sold]로 변환

    body_cars = [Car(make=make, model=model, year=year, price=price, engine=engine, autonomous=autonomous, sold=sold)]

    if len(body_cars) < 1:
        raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="No cars to add.")
    
    min_id = len(cars) + 1  # 다음 ID를 계산
    for car in body_cars:
        while cars.get(min_id):
            min_id += 1
        cars[min_id] = jsonable_encoder(car)
        min_id += 1

    return RedirectResponse(url="/cars", status_code=302)
  • def add_cars(....) 함수는 매개변수를 Form 형태로 make ~ min_id까지 받는다.
  • year 변수를 굳이 str로 받는 이유?!!!! 너무 궁금했는데요..

🍀clova says🍀

  • 예를들어 입력할 때 "2010년"이라고 쓰는 사람이 있음

  • 그러면 처음부터 int로 받아버리면 오류남

  • 그래서 str로 받아서 아래 try에서 처럼 int(year)로 바꿔주면 "2010년" 중에 2010만 뽑아와서 year 변수에 저장한다~!~!

  • 수업내내 궁금했는데 해결 완료 ><

👩‍🎓 edit 기능

@app.get("/edit", response_class=HTMLResponse)  
def edit_car(request: Request, id: int = Query(...)): 
    car = cars.get(id)  
    if not car:  
        return templates.TemplateResponse("search.html", {"request": request, "id": id, "car": car, "title": "Edit Car"}, status_code=status.HTTP_404_NOT_FOUND) 
    return templates.TemplateResponse("edit.html", {"request": request, "id": id, "car": car, "title": "Edit Car"})  # 템플릿 응답 반환


@app.post("/cars/{id}")  
def update_car(request: Request, id: int,
    make: Optional[str] = Form(None),  
    model: Optional[str] = Form(None),
    year: Optional[str] = Form(None),
    price: Optional[float] = Form(None),
    engine: Optional[str] = Form(None),
    autonomous: Optional[bool] = Form(None),
    sold: Optional[str] = Form(None),  
):
    stored = cars.get(id)
    if not stored:
        return templates.TemplateResponse("search.html", {"request": request, "id": id, "car": stored, "title": "Edit Car"}, status_code=status.HTTP_404_NOT_FOUND)

    try:
        year = int(year) if year is not None else stored['year']
    except ValueError:
        raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="Year must be an integer.")

    # sold 값을 리스트로 변환
    if sold is not None:
        sold = [sold] if isinstance(sold, str) else sold

    updated_data = Car(
        make=make or stored.get('make'),
        model=model or stored.get('model'),
        year=year,
        price=price if price is not None else stored.get('price'),
        engine=engine or stored.get('engine'),
        autonomous=autonomous if autonomous is not None else stored.get('autonomous'),
        sold=sold or stored.get('sold')
    )

    cars[id] = jsonable_encoder(updated_data)

    return RedirectResponse(url="/cars", status_code=302)
  • 위에서 설명한 코드는 설명생략
  • stored = cars.get(id) 이부분이 살짝 이해가 잘 안되는데... 나의 궁금증!
  • Q. cars에는 id가 여러개 있는데 그 id를 전부 가지고 와서 stored에 저장하려면 리스트 형태여야 함. 근데 여기서는 하나만 가지고 왔는데...
  • 아~~~ 바로 이해완
  • 내가 수정할 자동차의 id를 내가 선택했으니까 그 id값만 저장이 되겠네! babo~!
  • sold는 판매 지역을 뜻하는 변수인데 다중 선택이 가능한 체크박스라 값을 리스트로 변환 해준다!
  • sold = [sold] if isinstance(sold, str) else sold : sold가 str이면 리스트로 변환하고, 아니면 그냥 sold 그 자체로 반환
  • updated_data = Car( 어쩌구 저쩌구 ) : 복잡해 보이지만 사용자가 업데이트 한 정보 or 원래 정보를 조합 해서 edit 한다는 뜻
  • 원래 정보는 왜 조합? edit 안한게 있을 수도 있으니까~!
  • cars[id] = jsonable_encoder(updated_data) : jsonable_encoder 함수를 사용해서 updated_data를 JSON 형식으로 변환하고 cars 딕셔너리에 id 키와 함께 저장한다~!
  • cars[id]는 해당 id의 ^값^만 가져오니까 updated_data를 저장해 준다.

👩‍🎓 delete 기능

@app.get("/delete/{id}", response_class=RedirectResponse)  
def delete_car(request: Request, id: int = Path(...)):  
    if not cars.get(id):  
        return templates.TemplateResponse("search.html", {"request": request, "id": id, "title": "Edit Car"}, status_code=status.HTTP_404_NOT_FOUND)  
    del cars[id]  
    return RedirectResponse(url="/cars")  
  • 달리 설명할게 없네염

2. database.py

  • sqlalchemy 사용 안하고 파일 내에서 만들어서 연동함
  • main.py에서 import 했던 cars 객체가 여기에!
  • 6번은 내가 덧붙임 ><
cars = {
    1: {
        "make": "CarBrand",
        "model": "Fast",
        "year": 1998,
        "price": 25000.0,
        "engine": "V8",
        "autonomous": False,
        "sold": ["NA","EU"]
    },

    2: {
        "make": "Speedy",
        "model": "FourWheeler SUV",
        "year": 2021,
        "price": 55400.0,
        "engine": "V4",
        "autonomous": False,
        "sold": ["AF","AN","AS","EU","NA","OC","SA"]
    },

    3: {
        "make": "Elektrik",
        "model": "AutoCar",
        "year": 2019,
        "price": 45000.0,
        "engine": "V8",
        "autonomous": True,
        "sold": ["AS"]
    },

    4: {
        "make": "CarBrand",
        "model": "Beetle",
        "year": 2004,
        "price": 21299.99,
        "engine": "V4",
        "autonomous": False,
        "sold": []
    },

    5: {
        "make": "CarPro",
        "model": "Supersonic",
        "year": 2015,
        "price": 215000.0,
        "engine": "V12",
        "autonomous": False,
        "sold": ["NA","AF","OC","SA"]
    },
    6: {
        "make": "Ham",
        "model": "hanny",
        "year": 1998,
        "price": 'INF',
        "engine": "V12",
        "autonomous": False,
        "sold": ["NA","AF","OC","SA"]
    }
}


2. 딥러닝 + 웹 _ dog breed predict

  1. 코랩에서 딥러닝 모델 만들어서 다운로드 받기
  2. vscode에서 다운로드 받은 모델 가져와서 쓰기

3. 딥러닝 + 웹 _ face detect

0개의 댓글