Hoppa till huvudinnehållet

Bygg en realtidsbaserad uppgiftshanterare med FastHTML och MongoDB

En komplett handledning om att använda Python‑inhemska verktyg för asynkrona CRUD‑operationer och HTMX‑interaktivitet.
Uppdaterad 17 juni 2026  · 5 min läsa

FastHTML och MongoDB erbjuder ett snabbfotat, Python-inhemskt arbetssätt för modern webbutveckling. I den här handledningen bygger vi en reaktiv uppgiftshanterare i realtid och demonstrerar en komplett CRUD‑livscykel (Create, Read, Update, Delete) i en enda, lättskött Python-fil. För praktisk erfarenhet av MongoDB i Python rekommenderar jag kursen Introduction to MongoDB in Python.

Vad är FastHTML?

FastHTML är ett minimalistiskt, högpresterande ramverk byggt på grunderna i FastAPI. Det introducerar ett Pythoniskt HTML‑paradigm som gör det möjligt för utvecklare att bygga hela frontender med återanvändbara Python-funktioner i stället för traditionella mallar.

Dess kärnstyrka ligger i den inbyggda integreringen med HTMX. Genom att använda enkla HTML‑attribut för att driva uppdateringar på serversidan kan HTMX skapa dynamiska upplevelser som i en single-page‑applikation, utan komplexiteten i en tung JavaScript‑byggstack.

Vad är MongoDB?

MongoDB är den ledande allmänna, dokumentbaserade NoSQL‑databasen. Dess flexibla schema och användning av JSON‑liknande BSON‑dokument gör den idealisk för modern, iterativ utveckling.

För Python använder vi den officiella asynkrona drivrutinen Motor, som erbjuder ett icke‑blockerande gränssnitt perfekt anpassat för FastAPI:s och FastHTML:s prestandaorienterade arkitektur.

Varför den här stacken utmärker sig

Att kombinera dessa tekniker skapar en synnerligen produktiv utvecklingsmiljö:

  • Pydantic och MongoDB-typsäkerhet: FastHTML använder Pydantic för datamodellering och validering. Dessa modeller mappar naturligt till MongoDB:s dokumentstruktur och ger en ”code‑first”-upplevelse som eliminerar behovet av omfattande ORM‑mallskelett.
  • Ändan‑till‑ände asynkron prestanda: Genom att para ihop Motor med FastHTML:s asynkrona kärna blockeras aldrig händelseloopen av databasoperationer. Det säkerställer hög samtidighet och låg latens, vilket är avgörande för reaktiva applikationer i realtid.
  • Mindre kontextväxling: Utvecklare kan hantera databasschema, backend‑logik och frontend‑komponenter inom ett enhetligt Python‑ekosystem, vilket kraftigt ökar leveranstakten.

Konfiguration och anslutning

Det finns några förkunskaper du behöver för att följa den här handledningen: 

  • Python 3.8+
  • MongoDB-instans: en lokal installation eller ett MongoDB Atlas‑kluster (rekommenderas för realtidsfunktioner)
  • Grundläggande kunskaper: viss vana vid Python‑dekoratorer och grundläggande HTML‑struktur.

Projektinitiering

För att visa hur effektiv den här stacken är implementerar vi hela applikationen, inklusive databas­konfiguration, datamodeller och reaktiva rutter, i en enda app.py‑fil.

Installation 

Vi behöver ramverket FastHTML, Motor (den officiella asynkrona MongoDB‑drivrutinen) och Uvicorn för ASGI‑servern.

Konfiguration av MongoDB‑anslutning

Vi använder AsyncIOMotorClient för att upprätta en icke‑blockerande anslutning. Det säkerställer att applikationen kan fortsätta hantera andra samtidiga förfrågningar medan den väntar på databas‑I/O.

import os
from motor.motor_asyncio import AsyncIOMotorClient
from fasthtml.common import *
from bson import ObjectId
from pydantic import Field, BaseModel
from pymongo.errors import ServerSelectionTimeoutError, ConnectionFailure


# configure MongoDB
MONGO_URI = os.environ.get("MONGO_URI", "mongodb://localhost:27017/") # your Mongo URI
DB_NAME = "fasthtml_tasks_db"
COLLECTION_NAME = "tasks"

# Initialize the async client and reference our collections
client = AsyncIOMotorClient(MONGO_URI)
db = client[DB_NAME]
collection = db[COLLECTION_NAME]

# FastHTML Application Initialization
app = FastHTML()

Definiera datamodellen 

I ett dokumentorienterat arbetssätt ligger schemat i din applikationskod. Vi använder Pydantic v2 för att överbrygga klyftan mellan MongoDB:s BSON‑ObjectId och vanliga Python‑strängar. Det säkerställer att varje dokument som går in i eller lämnar databasen valideras mot våra krav.

Vi definierar en anpassad klass PyObjectId. Detta är nödvändigt eftersom Pydantic inte inbyggt vet hur den ska hantera typen ObjectId i MongoDB. Med __get_pydantic_core_schema__ talar vi om för Pydantic att behandla denna typ som en sträng i JSON men validera den som ett BSON‑objekt för databasen.

python
from pydantic import BaseModel, Field
from pydantic_core import core_schema
from pydantic.json_schema import JsonSchemaValue

class PyObjectId(ObjectId):
    """Custom type to bridge MongoDB ObjectId and Pydantic v2 validation."""
    @classmethod
    def __get_pydantic_core_schema__(cls, source_type, handler):
        # Defines how Pydantic should validate this type
        return core_schema.no_info_plain_validator_function(cls.validate)

    @classmethod
    def validate(cls, v):
        if isinstance(v, ObjectId): return v
        if isinstance(v, str) and ObjectId.is_valid(v): return ObjectId(v)
        raise ValueError("Invalid ObjectId")

    @classmethod
    def __get_pydantic_json_schema__(cls, _core_schema, handler) -> JsonSchemaValue:
        # Ensures the OpenAPI/JSON schema reflects this as a string
        return {"type": "string"}

class Task(BaseModel):
    """Pydantic model representing the Task document schema."""
    # Maps MongoDB's internal '_id' to a developer-friendly 'id' field
    id: PyObjectId | None = Field(None, alias='_id')
    title: str
    description: str | None = None
    completed: bool = False

    model_config = ConfigDict(
        arbitrary_types_allowed=True
    )

Layout och första rutt 

Layoutkomponenten är en återanvändbar högre ordningens funktion som omsluter våra vyer. Det säkerställer att nödvändiga beroenden, som Tailwind CSS för styling och HTMX för interaktivitet, är konsekventa i hela applikationen.

Genom att använda FastHTML:s komponenter med inledande versal (som Main och Div) behåller vi en ren, Pythonisk struktur som speglar HTML‑trädet.

# A responsive layout using Tailwind CSS and the HTMX CDN
def layout(*comps):
    """Wraps the application content in a consistent container."""
    return Main(
        Div(
            H1('📝 Real-Time FastHTML Task Manager', 
               cls='text-3xl font-bold mb-8 text-center text-gray-800'),
            *comps,
            cls='container mx-auto p-6 max-w-2xl'
        )
    )

@app.get('/')
async def home():
    """Initial route rendering the core application layout."""
    return layout(
        await TaskList(), # Fetches and displays existing tasks (Read)
        TaskForm()        # Renders the entry form (Create)
    )

Om du kör appen nu kommer gränssnittet att se ut så här:

Bild: skärmdump efter grundinställning

Det finns inga uppgifter ännu. Vi kommer att lägga till och uppdatera uppgifter i nästa avsnitt.

Fullständig CRUD‑implementation

Read: Visa uppgifter (GET /)

För att visa uppgifter hämtar vi alla dokument och renderar dem i en komponent. I ett verkligt scenario kan dock databasen vara offline. Vår uppdaterade TaskList hanterar detta smidigt genom att ge felsökningstips direkt i gränssnittet.

Robust datahämtning 

Vi använder collection.find().to_list(length=None) för att hämta dokument asynkront. Genom att kapsla in detta i ett try/except‑block kan vi upptäcka om MongoDB är frånkopplat och ge användaren omedelbar återkoppling.

async def TaskList():
    """Fetches documents from MongoDB and hydrates the Task list view with error handling."""
    try:
        tasks_data = await collection.find().to_list(length=None)
        tasks = [Task(**doc) for doc in tasks_data]
    except (ServerSelectionTimeoutError, ConnectionFailure, Exception) as e:
        # Catch MongoDB connection errors and provide troubleshooting tips
        return Div(
            H2('Current Tasks', cls='text-xl font-semibold mb-4'),
            Div(
                P('⚠️ MongoDB is not running.', cls='text-red-600 font-semibold mb-2'),
                P('To start MongoDB:', cls='text-gray-600 mb-1'),
                P('1. brew install mongodb-community', cls='text-gray-500 text-sm ml-4'),
                P('2. brew services start mongodb-community', cls='text-gray-500 text-sm ml-4'),
                cls='p-4 bg-yellow-50 border border-yellow-200 rounded'
            ),
            id='task-list'
        )

    return Div(
        H2('Current Tasks', cls='text-xl font-semibold mb-4'),
        *[TaskItem(task) for task in tasks] if tasks else P('No tasks yet. Add one below!', cls='text-gray-500 italic p-4'),
        id='task-list',
        cls='bg-white rounded-lg shadow-sm border border-gray-200'
    )

Create: Lägga till en ny uppgift

Skapandeflödet använder ett HTML‑formulär förstärkt med HTMX‑attribut. I den uppdaterade versionen extraherar vi uttryckligen formulärdata från Request‑objektet och använder model_dump för att förbereda dokumentet för insättning i MongoDB.

@app.post('/add-task')
async def add_task(req: Request):
    """Create: Inserts a task and returns the refreshed list."""
    try:
        form_data = await req.form()
        task = Task(
            title=form_data.get('title', ''),
            description=form_data.get('description') or None
        )
        
        # model_dump(by_alias=True) ensures 'id' is converted back to '_id' for Mongo
        task_dict = task.model_dump(exclude_none=True, by_alias=True)
        
        # Remove _id if it's None so MongoDB can generate its own unique ID
        if '_id' in task_dict and task_dict['_id'] is None:
            del task_dict['_id']
            
        await collection.insert_one(task_dict)
        return await TaskList()
    except Exception as e:
        return Div(P(f'Error: {str(e)}', cls='text-red-500 p-4'), id='task-list')

Om vi kör koden nedan i terminalen:

curl -X POST http://localhost:8000/add-task -d "title=Complete FastHTML MongoDB integration" -d "description=Verify that tasks can be created, updated, and deleted successfully" -H "Content-Type: application/x-www-form-urlencoded"

kommer du att se att den nya uppgiften har lagts till:

Bild: skärmdump efter att en uppgift lagts till

Update: Växla färdigstatus

För att hantera uppdateringar använder vi en PATCH‑begäran. Detta demonstrerar en ”mikrouppdatering”, där endast den enskilda uppgiftsraden uppdateras i databasen och renderas om i UI:t med hjälp av dess specifika ID.

@app.patch('/toggle-task/{task_id}')
async def toggle_task(task_id: str):
    """Update: Toggles completion status and returns the single row fragment."""
    task_doc = await collection.find_one({"_id": ObjectId(task_id)})
    if not task_doc: raise HTTPException(404, "Task not found")

    await collection.update_one(
        {"_id": ObjectId(task_id)},
        {"$set": {"completed": not task_doc['completed']}}
    )

    # Return only the updated TaskItem for a surgical DOM update
    updated_doc = await collection.find_one({"_id": ObjectId(task_id)})
    return TaskItem(Task(**updated_doc))

Delete: Ta bort en uppgift

Borttagning initieras av HTMX. När MongoDB bekräftar borttagningen returnerar servern ett Empty()‑svar. HTMX tolkar detta tomma svar som en signal att ta bort mål­elementet (närmaste div) från DOM:en.

@app.delete('/delete-task/{task_id}')
async def delete_task(task_id: str):
    """Delete: Removes a task from MongoDB."""
    await collection.delete_one({"_id": ObjectId(task_id)})
    # Signal HTMX to remove the element
    return Empty()

Om vi kör…

curl -X DELETE http://localhost:8000/delete-task/695968244236010c04f313fa

…tas uppgiften bort.

Bild: skärmdump efter borttagning 

Du hittar hela skriptet på GitHub.

Slutsats

Genom att utnyttja en modern, Python‑centrerad stack har du byggt en reaktiv applikation som undviker den traditionella komplexiteten i klientbaserade JavaScript‑ramverk. Denna specifika arkitektur ger flera viktiga fördelar för modern webbutveckling. Synergin mellan FastHTML:s komponentbaserade UI och MongoDB:s flexibla dokumentmodell gör att du kan hålla affärslogik, dataintegritet och presentation i ett enda, sammanhållet ekosystem. Detta ”allt‑i‑Python”-angreppssätt minskar utvecklings­bördan och driftsättnings­komplexiteten avsevärt.

Nästa steg för läsare

  • Användarautentisering: Använd FastAPI‑beroenden för att begränsa uppgiftslistor till specifika användare och lagra user_id i uppgiftsdokumentet.
  • Avancerade frågor: Använd MongoDB:s aggregationsramverk eller enkla filter för att lägga till vyerna ”Aktiva” och ”Slutförda” i din UI.
  • Driftsättning: Driftsätt din app.py med Uvicorn bakom en omvänd NGINX‑proxy för prestanda på produktionsnivå.

FAQs

Är det möjligt att använda MongoDB Change Streams med FastHTML för ”push”-uppdateringar?

Ja! Eftersom både Motor och FastHTML är asynkrona kan du använda en Python‑async for‑loop för att lyssna på en MongoDB Change Stream. Du kan sedan kombinera detta med FastHTML:s EventStream (Server‑Sent Events) för att skicka realtidsuppdateringar till alla anslutna användare när ett dokument ändras i databasen.

Varför använda Pydantic‑modeller i stället för vanliga Python‑ordböcker med MongoDB?

Även om MongoDB accepterar råa ordböcker fungerar Pydantic som ditt ”applikationsschema”. Det ger datavalidering, typhintar och standardvärden (som att sätta completed till False automatiskt). Det förhindrar ”smutsig data” från att hamna i din samling och gör din kod mycket enklare att felsöka när den växer.

Hur hanterar jag databas­migrationer med den här stacken?

En av MongoDB:s största styrkor är dess flexibla schema. Du behöver inte ”migrationer” i traditionell SQL‑bemärkelse. Om du lägger till ett nytt fält i din Task‑modell kan du helt enkelt ange ett standardvärde i Pydantic. Befintliga dokument i MongoDB som saknar det fältet kommer att ”hydreras” med standardvärdet när de laddas in i din applikation.

Kan jag lägga till komplexa sökfunktioner i den här uppgiftshanteraren?

Absolut. MongoDB har ett kraftfullt $text‑index och ett ännu mer avancerat Atlas Search (baserat på Lucene). Du kan enkelt skapa en sökruta i FastHTML med hx-get som triggar en MongoDB‑aggregationspipeline för att filtrera uppgifter efter nyckelord medan användaren skriver.

Hur hanterar den här stacken hög samtidighet jämfört med Django eller Flask?

FastHTML är ett separat ramverk inspirerat av FastAPI. Det använder ASGI‑standarden och kan hantera tusentals samtidiga anslutningar i en enda process. I kombination med Motors icke‑blockerande anslutningspoolning kommer din app inte att ”fastna” och vänta på databassvar, vilket gör den mycket mer effektiv för högtrafikerade, realtidsbaserade appar.

Ämnen

Top DataCamp Courses

track

Dataingenjör i Python

40 timmar
Få efterfrågade färdigheter för att effektivt ta in, rensa och hantera data samt schemalägga och övervaka pipelines, så att du sticker ut inom data engineering.
Se detaljerRight Arrow
Starta kursen
Se merRight Arrow