Створення застосунку на FastAPI з MongoDB: покроковий посібник

pic

У цьому пості ми пройдемо через створення бекенду на базі 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. Перший крок — це змінити структуру файлів, щоб вона виглядала так:

pic

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

Leave a Reply

Your email address will not be published. Required fields are marked *