У цьому пості ми пройдемо через створення бекенду на базі FastAPI, який підключається до бази даних MongoDB. Ми перейдемо від Flask до FastAPI. Основна увага буде приділена налаштуванню CRUD-ендпоінтів для створення, читання, оновлення та видалення елементів у базі даних. Наприкінці ми отримаємо повністю функціональний API, готовий для інтеграції з будь-яким фронтендом.
Перш ніж почати будувати наш API, давайте розглянемо перехід від Flask до FastAPI. Flask відмінно підходить для дуже простих завдань, але FastAPI пропонує сучасні функції, такі як підказки типів, автоматичну документацію та асинхронне програмування.
Ви можете наздогнати те, де починається цей посібник, прочитавши ці два статті:
[
Основна настройка Flask з Podman
Мені потрібно було налаштувати Python Flask додаток для бекенд-сервісу.
medium.com
](/@brianmayrose/basic-flask-setup-with-podman-6024ba6769e5?source=post_page-----b62ecc7f053c--------------------------------)
[
Додавання MongoDB до Flask
Для цього проекту я вибрав використовувати Podman замість Docker. Якщо ви не знайомі з Podman, не хвилюйтеся, Podman також…
medium.com
](/@brianmayrose/adding-mongodb-to-flask-087966930eee?source=post_page-----b62ecc7f053c--------------------------------)
Ми будемо запускати MongoDB всередині контейнера, використовуючи Podman. Щоб зупинити, запустити, перебудувати і перезапустити mongo pod, можна використовувати ці команди в один рядок:
podman stop mongodb && \
podman rm mongodb && \
podman build -t mongodb:latest -f mongodb/Dockerfile . && \
podman run --name mongodb \
-p 27017:27017 \
-v mongodb_data:/data/db \
-d docker.io/mongo:6.0
Це запустить сервер бази даних, доступний на порту 27017. Можливо, вам доведеться налаштувати імена, залежно від того, що ви виберете для вашого проекту.
Також потрібно оновити Docker файл для FastAPI таким чином:
# Базовий образ
FROM python:3.10-slim
# Встановлення робочої директорії
WORKDIR /app
# Встановлення залежностей
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Копіювання коду додатка
COPY . .
# Відкриття порту для FastAPI
EXPOSE 8000
# Запуск додатка з Uvicorn
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
Конвертація дуже проста, оскільки як Flask, так і FastAPI написані на Python. Перший крок — це змінити структуру файлів, щоб вона виглядала так:
https://tree.nathanfriend.com/
Наступний крок — замінити ініціалізатор додатка Flask на цю просту конфігурацію FastAPI у файлі main.py:
from app import app
Щоб зберегти проект організованим, ми оновимо config.py, який буде використовуватися як допоміжний файл для керування конфігураціями бази даних, які можна використовувати багаторазово. Додайте це в config.py:
# Конфігурація MongoDB
MONGO_URI = "mongodb://mongodb:27017/" # 'mongodb' відноситься до імені контейнера в мережі
DATABASE_NAME = "interactive_portfolio"
Тепер ми можемо оновити файл init.py, щоб зв'язати базу даних з бекендом:
from fastapi import FastAPI
from pymongo import MongoClient
# Імпорт конфігурації
from .config import MONGO_URI, DATABASE_NAME
# Ініціалізація FastAPI додатка
app = FastAPI()
# Підключення до MongoDB (спільне для всього додатка)
client = MongoClient(MONGO_URI)
db = client[DATABASE_NAME]
# Реєстрація маршрутів
from .routes import router
app.include_router(router)
# Глобальний доступ до бази даних для використання в сервісах або маршрутах
__all__ = ["app", "db"]
Також потрібно оновити requirements.txt:
fastapi
uvicorn
pymongo
pydantic
Наступний крок — визначити структуру даних, яка буде в нашій базі даних. Ми помістимо моделі даних у models.py. Для управління ресурсами ми використовуємо Pydantic для валідації даних.
Оновіть models.py наступним чином:
from bson import ObjectId
from pydantic import BaseModel, Field, GetCoreSchemaHandler
from pydantic.json_schema import JsonSchemaValue
from typing import Optional
# Допоміжний клас для MongoDB ObjectId
class PyObjectId(ObjectId):
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(clssRef, value_to_validate):
if not ObjectId.is_valid(value_to_validate):
raise ValueError("Невірний ObjectId")
return ObjectId(value_to_validate)
@classmethod
def __get_pydantic_json_schema__(cls, schema: JsonSchemaValue, handler: GetCoreSchemaHandler) -> JsonSchemaValue:
# Визначення JSON-схеми для ObjectId
schema = handler(schema)
schema.update(type="string")
return schema
@classmethod
def __get_pydantic_core_schema__(cls, source, handler: GetCoreSchemaHandler):
# Використовуємо основну схему для валідації ObjectId
return handler(str) # Обробляємо ObjectId як рядок для валідації
# Модель Pydantic для Item
class Item(BaseModel):
id: Optional[PyObjectId] = Field(alias="_id") # ObjectId MongoDB
name: str = Field(..., description="Назва/тип елемента")
category: str = Field(..., description="Категорія елемента")
rating: int = Field(..., ge=0, le=10, description="Рівень оцінки, від 0 до 10")
user_id: PyObjectId = Field(..., description="ID користувача, до якого належить цей елемент")
class Config:
allow_population_by_field_name = True
arbitrary_types_allowed = True
json_encoders = {ObjectId: str}
Створивши модель, ми можемо створити маршрути, які оброблятимуть CRUD операції для елементів.
Оновіть routes.py наступним чином:
from fastapi import APIRouter, HTTPException
from app import db
from app.models import Item, PyObjectId
from bson import ObjectId
from typing import Optional
router = APIRouter()
@router.get("/item", response_model=list[Item])
async def get_items(user_id: PyObjectId):
"""Отримати всі елементи для конкретного користувача."""
items = db["items"].find({"user_id": ObjectId(user_id)})
return [Item(**item) for item in items]
@router.get("/items/{item_id}", response_model=Item)
async def get_item(item_id: PyObjectId):
"""Отримати одиничний елемент за його ID."""
item = db["items"].find_one({"_id": ObjectId(item_id)})
if item:
return Item(**item)
raise HTTPException(status_code=404, detail="Елемент не знайдено")
@router.post("/items", response_model=Item)
async def create_item(item: Item):
"""Створити новий елемент."""
try:
# Перетворити модель Item на словник для MongoDB
item_dict = item.dict(by_alias=True)
# Вставити елемент у базу даних
result = db["item"].insert_one(item_dict)
# Отримати щойно вставлений документ
inserted_item = db["items"].find_one({"_id": result.inserted_id})
# Повернути вставлений документ як модель Pydantic
return Item(**inserted_item)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Не вдалося створити елемент: {str(e)}")
@router.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: PyObjectId, name: Optional[str] = None, category: Optional[str] = None, proficiency: Optional[int] = None):
"""Оновити існуючий елемент за ID."""
update_data = {}
# Додаємо тільки поля, які надано
if name is not None:
update_data["name"] = name
if category is not None:
update_data["category"] - category
if rating is not None:
if not (0 <= rating <= 10):
raise HTTPException(status_code=400, detail="Рейтинг повинен бути між 0 та 10")
update_data["rating"] = rating
if not update_data:
raise HTTPException(status_code=400, detail="Не надано дані для оновлення")
# Оновити елемент у базі даних
result = db["items"].update_one({"_id": ObjectId(item_id)}, {"$set": update_data})
if result.matched_count == 0:
raise HTTPException(status_code=404, detail="Елемент не знайдено")
# Отримати оновлений елемент
updated_item = db["items"].find_one({"_id": ObjectId(item_id)})
if updated_item:
return Item(**updated_item)
else:
raise HTTPException(status_code=500, detail="Не вдалося отримати оновлений елемент")
@router.delete("/items/{item_id}", response_model=dict)
async def delete_item(item_id: PyObjectId):
"""Видалити елемент за його ID."""
result = db["items"].delete_one({"_id": ObjectId(item_id)})
if result.deleted_count == 0:
raise HTTPException(status_code=404, detail="Елемент не знайдено")
return {"message": "Елемент успішно видалено"}
Подібно до зупинки, запуску, перебудови та перезапуску mongo pod, ми можемо зробити те саме для API бекенду в один рядок:
podman stop fastapi-backend && \
podman rm fastapi-backend && \
podman build -t fastapi-backend:latest backend/ && \
podman run --name fastapi-backend \
--network project-network \
-p 8000:8000 \
fastapi-backend:latest
Ми щойно пройшли процес налаштування ефективного, високопродуктивного бекенду за допомогою FastAPI, підключили його до бази даних MongoDB і реалізували повний спектр функцій CRUD. Як я зазначав у попередніх постах, я також продовжуватиму досліджувати, як зробити відкриті інструменти корисними для вас завдяки продуманій організації, масштабованості та простоті.
Як показано, FastAPI — це не просто фреймворк, це інструмент, який дозволяє розробникам писати чистіший, швидший і підтримуваний код.
Знайомлячись з такими можливостями, як моделі Pydantic для валідації даних, асинхронне програмування для підвищення швидкості та автоматична документація API, ви можете створювати основи проекту, які не лише функціональні, але й зручні для розробників.
MongoDB — це неймовірно гнучка база даних для динамічних і змінних структур даних, відмінно підходить для нового розвитку. Використовуючи команди, такі як $set для оновлень і find_one для отримання, а також інші команди, ви можете зосередитися на наданні рішень, а не боротися зі складними схемами. Краса цієї налаштування в тому, що процес масштабування його до повноцінного застосунку буде приємним досвідом.
Тому, продовжуючи, пам'ятайте про модульність, розділяючи ваш код на керовані частини, зберігаючи організованість, коли ваш застосунок росте в розмірах і складності. Ознайомтесь з інструментами, такими як Pydantic, щоб виявляти проблеми з даними ще до того, як вони потраплять до бази даних. Тримайте все якнайлегше, використовуючи контейнери з Podman або Docker, і отримуйте максимум від документації, створеної з Swagger.
Перекладено з: Building a FastAPI Application with MongoDB: A Step-by-Step Guide