add: database cars, enpoints

This commit is contained in:
2025-07-07 19:38:22 +05:00
parent 5679f02142
commit aeb613cda8
8 changed files with 319 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__
venv

View File

@ -0,0 +1,42 @@
# AutoBro Backend
API сервер для хранения и управления данными об автомобилях.
## Структура проекта
- **main.py** - основной файл с API эндпоинтами
- **models.py** - модели SQLAlchemy для базы данных
- **schemas.py** - схемы Pydantic для валидации данных
- **database.py** - настройка подключения к базе данных
- **crud.py** - функции для CRUD операций
## Запуск проекта
1. Установите зависимости:
```bash
pip install fastapi uvicorn sqlalchemy pydantic
```
2. Запустите сервер:
```bash
uvicorn main:app --reload
```
Сервер будет доступен по адресу http://localhost:8000
## API эндпоинты
- **GET /cars** - получить список всех автомобилей
- **GET /cars/{car_id}** - получить информацию о конкретном автомобиле
- **POST /cars** - добавить новый автомобиль
- **PUT /cars/{car_id}** - обновить информацию об автомобиле
- **DELETE /cars/{car_id}** - удалить автомобиль
## Документация API
После запуска сервера документация доступна по адресам:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc

BIN
cars.db Normal file

Binary file not shown.

74
crud.py Normal file
View File

@ -0,0 +1,74 @@
from sqlalchemy.orm import Session
from models import Car, EngineType, HybridType, PowerRatio
import schemas
from typing import List, Optional
def get_car(db: Session, car_id: int) -> Optional[Car]:
return db.query(Car).filter(Car.id == car_id).first()
def get_cars(db: Session, skip: int = 0, limit: int = 100) -> List[Car]:
return db.query(Car).offset(skip).limit(limit).all()
def get_cars_count(db: Session) -> int:
return db.query(Car).count()
def create_car(db: Session, car: schemas.CarCreate) -> Car:
# Преобразование из Pydantic enum в SQLAlchemy enum
engine_type = EngineType(car.engine_type.value)
hybrid_type = HybridType(car.hybrid_type.value)
power_ratio = PowerRatio(car.power_ratio.value)
db_car = Car(
image=car.image,
name=car.name,
price=car.price,
base_price=car.base_price,
country_of_origin=car.country_of_origin,
year=car.year,
drive_type=car.drive_type,
mileage=car.mileage,
engine_capacity=car.engine_capacity,
engine_power=car.engine_power,
engine_type=engine_type,
electric_motor_power=car.electric_motor_power,
hybrid_type=hybrid_type,
power_ratio=power_ratio,
)
db.add(db_car)
db.commit()
db.refresh(db_car)
return db_car
def update_car(db: Session, car_id: int, car_update: schemas.CarUpdate) -> Optional[Car]:
db_car = get_car(db, car_id)
if not db_car:
return None
update_data = car_update.model_dump(exclude_unset=True)
# Обработка enum полей
if "engine_type" in update_data and update_data["engine_type"] is not None:
update_data["engine_type"] = EngineType(update_data["engine_type"])
if "hybrid_type" in update_data and update_data["hybrid_type"] is not None:
update_data["hybrid_type"] = HybridType(update_data["hybrid_type"])
if "power_ratio" in update_data and update_data["power_ratio"] is not None:
update_data["power_ratio"] = PowerRatio(update_data["power_ratio"])
# Обновление полей
for key, value in update_data.items():
setattr(db_car, key, value)
db.commit()
db.refresh(db_car)
return db_car
def delete_car(db: Session, car_id: int) -> bool:
db_car = get_car(db, car_id)
if not db_car:
return False
db.delete(db_car)
db.commit()
return True

19
database.py Normal file
View File

@ -0,0 +1,19 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./cars.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

78
main.py Normal file
View File

@ -0,0 +1,78 @@
from fastapi import FastAPI, Depends, HTTPException, status, Query, Path
from sqlalchemy.orm import Session
import crud
import models
import schemas
from database import engine, get_db
from typing import List, Optional
import uvicorn
# Создание таблиц в БД
models.Base.metadata.create_all(bind=engine)
app = FastAPI(title="AutoBro API", description="API для управления базой данных автомобилей")
@app.get("/")
def read_root():
return {"message": "AutoBro API"}
@app.get("/cars", response_model=schemas.CarsResponse)
def get_cars(
skip: int = Query(0, description="Количество пропускаемых записей"),
limit: int = Query(100, description="Максимальное количество записей"),
db: Session = Depends(get_db)
):
cars = crud.get_cars(db, skip=skip, limit=limit)
total = crud.get_cars_count(db)
return {"cars": cars, "total": total}
@app.get("/cars/{car_id}", response_model=schemas.CarResponse)
def get_car(
car_id: int = Path(..., description="ID автомобиля", gt=0),
db: Session = Depends(get_db)
):
car = crud.get_car(db, car_id=car_id)
if car is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Автомобиль не найден"
)
return {"car": car}
@app.post("/cars", response_model=schemas.CarResponse, status_code=status.HTTP_201_CREATED)
def create_car(
car: schemas.CarCreate,
db: Session = Depends(get_db)
):
db_car = crud.create_car(db=db, car=car)
return {"car": db_car}
@app.put("/cars/{car_id}", response_model=schemas.CarResponse)
def update_car(
car_id: int = Path(..., description="ID автомобиля", gt=0),
car_update: schemas.CarUpdate = ...,
db: Session = Depends(get_db)
):
updated_car = crud.update_car(db=db, car_id=car_id, car_update=car_update)
if updated_car is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Автомобиль не найден"
)
return {"car": updated_car}
@app.delete("/cars/{car_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_car(
car_id: int = Path(..., description="ID автомобиля", gt=0),
db: Session = Depends(get_db)
):
success = crud.delete_car(db=db, car_id=car_id)
if not success:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Автомобиль не найден"
)
return None
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=3000)

37
models.py Normal file
View File

@ -0,0 +1,37 @@
from sqlalchemy import Boolean, Column, Float, Integer, String, Enum
import enum
from database import Base
class EngineType(enum.Enum):
PETROL = "бензиновый"
DIESEL = "дизельный"
ELECTRIC = "электрический"
class HybridType(enum.Enum):
NONE = "не гибрид"
HYBRID = "электрогибрид"
PHEV = "электрогибрид(PHEV)"
class PowerRatio(enum.Enum):
ICE_GREATER = "ДВС > ЭД"
ELECTRIC_GREATER = "ЭД > ДВС"
NA = "не применимо"
class Car(Base):
__tablename__ = "cars"
id = Column(Integer, primary_key=True, index=True)
image = Column(String, nullable=True) # путь к изображению или URL
name = Column(String, index=True)
price = Column(Float)
base_price = Column(Float) # цена без калькулирования
country_of_origin = Column(String)
year = Column(Integer)
drive_type = Column(String) # привод
mileage = Column(Integer, nullable=True) # пробег
engine_capacity = Column(Float, nullable=True) # объем двигателя
engine_power = Column(Integer, nullable=True) # мощность ДВС
engine_type = Column(Enum(EngineType)) # тип двигателя
electric_motor_power = Column(Integer, nullable=True) # мощность электродвигателя
hybrid_type = Column(Enum(HybridType), default=HybridType.NONE) # тип гибрида
power_ratio = Column(Enum(PowerRatio), default=PowerRatio.NA) # соотношение мощности

67
schemas.py Normal file
View File

@ -0,0 +1,67 @@
from pydantic import BaseModel, Field
from typing import Optional, List
from enum import Enum
from models import EngineType, HybridType, PowerRatio
class EngineTypeEnum(str, Enum):
PETROL = "бензиновый"
DIESEL = "дизельный"
ELECTRIC = "электрический"
class HybridTypeEnum(str, Enum):
NONE = "не гибрид"
HYBRID = "электрогибрид"
PHEV = "электрогибрид(PHEV)"
class PowerRatioEnum(str, Enum):
ICE_GREATER = "ДВС > ЭД"
ELECTRIC_GREATER = "ЭД > ДВС"
NA = "не применимо"
class CarBase(BaseModel):
image: Optional[str] = None
name: str
price: float
base_price: float
country_of_origin: str
year: int
drive_type: str
mileage: Optional[int] = None
engine_capacity: Optional[float] = None
engine_power: Optional[int] = None
engine_type: EngineTypeEnum
electric_motor_power: Optional[int] = None
hybrid_type: HybridTypeEnum = HybridTypeEnum.NONE
power_ratio: PowerRatioEnum = PowerRatioEnum.NA
class CarCreate(CarBase):
pass
class CarUpdate(BaseModel):
image: Optional[str] = None
name: Optional[str] = None
price: Optional[float] = None
base_price: Optional[float] = None
country_of_origin: Optional[str] = None
year: Optional[int] = None
drive_type: Optional[str] = None
mileage: Optional[int] = None
engine_capacity: Optional[float] = None
engine_power: Optional[int] = None
engine_type: Optional[EngineTypeEnum] = None
electric_motor_power: Optional[int] = None
hybrid_type: Optional[HybridTypeEnum] = None
power_ratio: Optional[PowerRatioEnum] = None
class Car(CarBase):
id: int
class Config:
from_attributes = True
class CarResponse(BaseModel):
car: Car
class CarsResponse(BaseModel):
cars: List[Car]
total: int