├── .gitignore ├── api.py ├── config.py ├── crud.py ├── database.py ├── exceptions.py ├── main.py ├── models.py └── schemas.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | __pycache__ 3 | .env -------------------------------------------------------------------------------- /api.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Depends, HTTPException 2 | from fastapi_utils.cbv import cbv 3 | from sqlalchemy.orm import Session 4 | from crud import get_all_cars, create_car, get_car_info_by_id, update_car_info, delete_car_info 5 | from database import get_db 6 | from exceptions import CarInfoException 7 | from schemas import Car, CreateAndUpdateCar, PaginatedCarInfo 8 | 9 | router = APIRouter() 10 | 11 | 12 | # Example of Class based view 13 | @cbv(router) 14 | class Cars: 15 | session: Session = Depends(get_db) 16 | 17 | # API to get the list of car info 18 | @router.get("/cars", response_model=PaginatedCarInfo) 19 | def list_cars(self, limit: int = 10, offset: int = 0): 20 | 21 | cars_list = get_all_cars(self.session, limit, offset) 22 | response = {"limit": limit, "offset": offset, "data": cars_list} 23 | 24 | return response 25 | 26 | # API endpoint to add a car info to the database 27 | @router.post("/cars") 28 | def add_car(self, car_info: CreateAndUpdateCar): 29 | 30 | try: 31 | car_info = create_car(self.session, car_info) 32 | return car_info 33 | except CarInfoException as cie: 34 | raise HTTPException(**cie.__dict__) 35 | 36 | 37 | # API endpoint to get info of a particular car 38 | @router.get("/cars/{car_id}", response_model=Car) 39 | def get_car_info(car_id: int, session: Session = Depends(get_db)): 40 | 41 | try: 42 | car_info = get_car_info_by_id(session, car_id) 43 | return car_info 44 | except CarInfoException as cie: 45 | raise HTTPException(**cie.__dict__) 46 | 47 | 48 | # API to update a existing car info 49 | @router.put("/cars/{car_id}", response_model=Car) 50 | def update_car(car_id: int, new_info: CreateAndUpdateCar, session: Session = Depends(get_db)): 51 | 52 | try: 53 | car_info = update_car_info(session, car_id, new_info) 54 | return car_info 55 | except CarInfoException as cie: 56 | raise HTTPException(**cie.__dict__) 57 | 58 | 59 | # API to delete a car info from the data base 60 | @router.delete("/cars/{car_id}") 61 | def delete_car(car_id: int, session: Session = Depends(get_db)): 62 | 63 | try: 64 | return delete_car_info(session, car_id) 65 | except CarInfoException as cie: 66 | raise HTTPException(**cie.__dict__) -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | from dotenv import load_dotenv 2 | import os 3 | 4 | load_dotenv() 5 | 6 | DATABASE_URL = os.getenv("MYSQL_DATABASE_URL") -------------------------------------------------------------------------------- /crud.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from sqlalchemy.orm import Session 3 | from exceptions import CarInfoInfoAlreadyExistError, CarInfoNotFoundError 4 | from models import CarInfo 5 | from schemas import CreateAndUpdateCar 6 | 7 | 8 | # Function to get list of car info 9 | def get_all_cars(session: Session, limit: int, offset: int) -> List[CarInfo]: 10 | return session.query(CarInfo).offset(offset).limit(limit).all() 11 | 12 | 13 | # Function to get info of a particular car 14 | def get_car_info_by_id(session: Session, _id: int) -> CarInfo: 15 | car_info = session.query(CarInfo).get(_id) 16 | 17 | if car_info is None: 18 | raise CarInfoNotFoundError 19 | 20 | return car_info 21 | 22 | 23 | # Function to add a new car info to the database 24 | def create_car(session: Session, car_info: CreateAndUpdateCar) -> CarInfo: 25 | car_details = session.query(CarInfo).filter(CarInfo.manufacturer == car_info.manufacturer, CarInfo.modelName == car_info.modelName).first() 26 | if car_details is not None: 27 | raise CarInfoInfoAlreadyExistError 28 | 29 | new_car_info = CarInfo(**car_info.dict()) 30 | session.add(new_car_info) 31 | session.commit() 32 | session.refresh(new_car_info) 33 | return new_car_info 34 | 35 | 36 | # Function to update details of the car 37 | def update_car_info(session: Session, _id: int, info_update: CreateAndUpdateCar) -> CarInfo: 38 | car_info = get_car_info_by_id(session, _id) 39 | 40 | if car_info is None: 41 | raise CarInfoNotFoundError 42 | 43 | car_info.manufacturer = info_update.manufacturer 44 | car_info.modelName = info_update.modelName 45 | car_info.fuelType = info_update.fuelType 46 | car_info.cc = info_update.cc 47 | car_info.gearBox = info_update.gearBox 48 | car_info.onRoadPrice = info_update.onRoadPrice 49 | car_info.seatingCapacity = info_update.seatingCapacity 50 | 51 | session.commit() 52 | session.refresh(car_info) 53 | 54 | return car_info 55 | 56 | 57 | # Function to delete a car info from the db 58 | def delete_car_info(session: Session, _id: int): 59 | car_info = get_car_info_by_id(session, _id) 60 | 61 | if car_info is None: 62 | raise CarInfoNotFoundError 63 | 64 | session.delete(car_info) 65 | session.commit() 66 | 67 | return 68 | -------------------------------------------------------------------------------- /database.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import sessionmaker 4 | import config 5 | 6 | DATABASE_URL = config.DATABASE_URL 7 | 8 | db_engine = create_engine(DATABASE_URL) 9 | SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=db_engine) 10 | 11 | Base = declarative_base() 12 | 13 | 14 | def get_db(): 15 | """ 16 | Function to generate db session 17 | :return: Session 18 | """ 19 | db = None 20 | try: 21 | db = SessionLocal() 22 | yield db 23 | finally: 24 | db.close() 25 | -------------------------------------------------------------------------------- /exceptions.py: -------------------------------------------------------------------------------- 1 | class CarInfoException(Exception): 2 | ... 3 | 4 | 5 | class CarInfoNotFoundError(CarInfoException): 6 | def __init__(self): 7 | self.status_code = 404 8 | self.detail = "Car Info Not Found" 9 | 10 | 11 | class CarInfoInfoAlreadyExistError(CarInfoException): 12 | def __init__(self): 13 | self.status_code = 409 14 | self.detail = "Car Info Already Exists" 15 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # main.py 2 | # Import FastAPI 3 | from fastapi import FastAPI 4 | from pydantic import BaseModel 5 | import uvicorn 6 | import api 7 | 8 | # Initialize the app 9 | app = FastAPI() 10 | 11 | app.include_router(api.router) 12 | 13 | 14 | # GET operation at route '/' 15 | @app.get('/') 16 | def root_api(): 17 | return {"message": "Welcome to Balasundar's Technical Blog"} 18 | -------------------------------------------------------------------------------- /models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.schema import Column 2 | from sqlalchemy.types import String, Integer, Enum 3 | from database import Base 4 | import enum 5 | 6 | 7 | class FuelType(str, enum.Enum): 8 | Petrol = "Petrol" 9 | Diesel = "Diesel" 10 | 11 | 12 | class CarInfo(Base): 13 | __tablename__ = "car" 14 | 15 | id = Column(Integer, primary_key=True, index=True) 16 | manufacturer = Column(String) 17 | modelName = Column(String) 18 | cc = Column(Integer) 19 | onRoadPrice = Column(Integer) 20 | seatingCapacity = Column(Integer) 21 | gearBox = Column(Integer) 22 | fuelType = Column(Enum(FuelType)) 23 | -------------------------------------------------------------------------------- /schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | from models import FuelType 3 | from typing import Optional, List 4 | 5 | 6 | # TO support creation and update APIs 7 | class CreateAndUpdateCar(BaseModel): 8 | manufacturer: str 9 | modelName: str 10 | cc: int 11 | onRoadPrice: int 12 | seatingCapacity: int 13 | gearBox: int 14 | fuelType: FuelType 15 | 16 | 17 | # TO support list and get APIs 18 | class Car(CreateAndUpdateCar): 19 | id: int 20 | 21 | class Config: 22 | orm_mode = True 23 | 24 | 25 | # To support list cars API 26 | class PaginatedCarInfo(BaseModel): 27 | limit: int 28 | offset: int 29 | data: List[Car] 30 | 31 | --------------------------------------------------------------------------------