Erstellen Sie einen perfekten Blog mit FastAPI: Besucheranalysen
Ethan Miller
Product Engineer · Leapcell

In einem früheren Artikel haben wir unserem Blog eine Volltextsuchfunktion hinzugefügt, um die Suche nach großartigen Inhalten zu erleichtern.
Jetzt, da Ihr Blog immer funktionsreicher wird und Ihre Inhalte wachsen, stellt sich natürlich eine neue Frage: Welche Artikel sind bei den Lesern am beliebtesten?
Das Verständnis der Interessen Ihrer Leser kann Ihnen helfen, qualitativ hochwertigere Inhalte zu erstellen.
Daher werden wir in diesem Tutorial eine grundlegende, aber sehr wichtige Funktion zu unserem Blog hinzufügen: Besucher-Tracking. Wir werden aufzeichnen, wie oft jeder Artikel gelesen wird, und die Aufrufzahl auf der Seite anzeigen.
Sie könnten in Erwägung ziehen, einen Drittanbieterdienst wie Google Analytics zu nutzen. Der Aufbau eines eigenen Backend-gesteuerten Tracking-Systems ermöglicht es uns jedoch, mehr Daten in unseren eigenen Händen zu behalten und anzupassen, welche Daten wir sammeln möchten.
Legen wir los:
Schritt 1: Erstellen Sie das Datenmodell für Seitenaufrufe
1. Erstellen Sie die Datenbanktabelle
Führen Sie die folgende SQL-Anweisung in Ihrer PostgreSQL-Datenbank aus, um die Tabelle pageview
zu erstellen. Diese Tabelle wird den Zeitpunkt jedes Aufrufs, den entsprechenden Beitrag und einige Besucherinformationen (wie IP-Adresse und User Agent) für zukünftige eingehende Analysen aufzeichnen.
CREATE TABLE "pageview" ( "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), "createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(), "postId" UUID REFERENCES "post"("id") ON DELETE CASCADE, "ipAddress" VARCHAR(45), "userAgent" TEXT );
Hinweis: ON DELETE CASCADE
stellt sicher, dass beim Löschen eines Beitrags alle zugehörigen Seitenaufrufdatensätze ebenfalls automatisch gelöscht werden.
Wenn Ihre Datenbank auf Leapcell erstellt wurde,
können Sie SQL-Anweisungen einfach über die grafische Benutzeroberfläche ausführen. Gehen Sie einfach zur Seite Datenbankverwaltung der Website, fügen Sie die obige Anweisung in die SQL-Oberfläche ein und führen Sie sie aus.
2. Erstellen Sie die PageView-Entität
Öffnen Sie als Nächstes die Datei models.py
, fügen Sie das PageView
-Modell hinzu und aktualisieren Sie das Post
-Modell, um eine bidirektionale Beziehung herzustellen.
# models.py import uuid from datetime import datetime from typing import Optional, List from sqlmodel import Field, SQLModel, Relationship # ... User Class ... class Post(SQLModel, table=True): id: Optional[uuid.UUID] = Field(default_factory=uuid.uuid4, primary_key=True) title: str content: str createdAt: datetime = Field(default_factory=datetime.utcnow, nullable=False) comments: List["Comment"] = Relationship(back_populates="post") # Add a one-to-many relationship with PageView page_views: List["PageView"] = Relationship(back_populates="post") # ... Comment Class ... class PageView(SQLModel, table=True): id: Optional[uuid.UUID] = Field(default_factory=uuid.uuid4, primary_key=True) createdAt: datetime = Field(default_factory=datetime.utcnow, nullable=False) ipAddress: Optional[str] = Field(max_length=45, default=None) userAgent: Optional[str] = Field(default=None) # Define the foreign key, linking to the Post table postId: uuid.UUID = Field(foreign_key="post.id") # Define the many-to-one relationship post: "Post" = Relationship(back_populates="page_views")
Da wir die Funktion create_db_and_tables
in main.py
konfiguriert haben, erkennt SQLModel beim Starten der Anwendung automatisch Modelländerungen und aktualisiert die Datenbanktabellenstruktur, sodass wir keine SQL-Abfragen manuell ausführen müssen.
Schritt 2: Implementieren Sie den Tracking-Dienst
Um den Code sauber zu halten, erstellen wir eine neue Dienstdatei für die Funktionalität des Seitenaufruf-Trackings.
Erstellen Sie eine neue Datei tracking_service.py
im Stammverzeichnis des Projekts, um die gesamte Logik für Seitenaufrufe zu verwalten.
# tracking_service.py import uuid from typing import List, Dict from sqlmodel import Session, select, func from models import PageView def record_view(post_id: uuid.UUID, ip_address: str, user_agent: str, session: Session): """Records a new page view""" new_view = PageView( postId=post_id, ipAddress=ip_address, userAgent=user_agent, ) session.add(new_view) session.commit() def get_count_by_post_id(post_id: uuid.UUID, session: Session) -> int: """Gets the total view count for a single post""" statement = select(func.count(PageView.id)).where(PageView.postId == post_id) # .one() or .one_or_none() is required for queries that return a single scalar value count = session.exec(statement).one_or_none() return count if count is not None else 0 def get_counts_by_post_ids(post_ids: List[uuid.UUID], session: Session) -> Dict[uuid.UUID, int]: """For efficiency, gets view counts for multiple posts at once""" if not post_ids: return {} statement = ( select(PageView.postId, func.count(PageView.id).label("count")) .where(PageView.postId.in_(post_ids)) .group_by(PageView.postId) ) results = session.exec(statement).all() # Convert the results to a dictionary of {post_id: count} format return {post_id: count for post_id, count in results}
Die Methode
get_counts_by_post_ids
verwendetfunc.count
undgroup_by
von SQLModel (SQLAlchemy), um eine effizienteGROUP BY
-Abfrage auszuführen. Dies ist wesentlich schneller, als für jeden Beitrag eine separatecount
-Abfrage auszuführen, insbesondere wenn die Homepage die Aufrufzahlen für mehrere Artikel anzeigen muss.
Schritt 3: Integrieren Sie die Aufzeichnung von Aufrufen auf der Artikelseite
Als Nächstes müssen wir die Methode record_view
des tracking_service
aufrufen, jedes Mal, wenn ein Besucher einen Beitrag aufruft. Der am besten geeignete Ort dafür ist die Route get_post_by_id
in routers/posts.py
.
Öffnen Sie routers/posts.py
, importieren Sie den neuen Dienst und rufen Sie ihn auf.
# routers/posts.py # ... other imports import tracking_service # Import the tracking service # ... @router.get("/posts/{post_id}", response_class=HTMLResponse) def get_post_by_id( request: Request, post_id: uuid.UUID, session: Session = Depends(get_session), user: dict | None = Depends(get_user_from_session), ): post = session.get(Post, post_id) if not post: # Handle post not found return HTMLResponse(status_code=404) comments = comments_service.get_comments_by_post_id(post_id, session) # Record a view (Fire-and-forget) client_ip = request.client.host user_agent = request.headers.get("user-agent", "") tracking_service.record_view(post_id, client_ip, user_agent, session) # Get the view count view_count = tracking_service.get_count_by_post_id(post_id, session) # Parse Markdown content post.content = markdown2.markdown(post.content) return templates.TemplateResponse( "post.html", { "request": request, "post": post, "title": post.title, "user": user, "comments": comments, "view_count": view_count, # Pass the view count to the template }, )
Schritt 4: Zeigen Sie Aufrufzahlen im Frontend an
Detailseite des Artikels
Im vorherigen Schritt haben wir bereits den view_count
abgerufen und ihn an die Vorlage post.html
übergeben. Jetzt müssen wir ihn nur noch in der Vorlage anzeigen.
Öffnen Sie templates/post.html
und fügen Sie die Aufrufzahl im Bereich der Metadaten des Beitrags hinzu:
<article class="post-detail"> <h1>{{ post.title }}</h1> <small>{{ post.createdAt.strftime('%Y-%m-%d') }} | Views: {{ view_count }}</small> <div class="post-content">{{ post.content | safe }}</div> </article>
Blog-Homepage
Um die Aufrufzahlen auch in der Beitragsliste auf der Homepage anzuzeigen, müssen wir die Route get_all_posts
anpassen.
Aktualisieren Sie routers/posts.py
:
# routers/posts.py # ... @router.get("/posts", response_class=HTMLResponse) def get_all_posts( request: Request, session: Session = Depends(get_session), user: dict | None = Depends(get_user_from_session) ): # 1. Get all posts statement = select(Post).order_by(Post.createdAt.desc()) posts = session.exec(statement).all() # 2. Get the IDs of all posts post_ids = [post.id for post in posts] # 3. Batch get the view counts view_counts = tracking_service.get_counts_by_post_ids(post_ids, session) # 4. Attach the view count to each post object for post in posts: post.view_count = view_counts.get(post.id, 0) return templates.TemplateResponse( "index.html", { "request": request, "posts": posts, "title": "Home", "user": user } ) # ...
Aktualisieren Sie schließlich die Vorlage templates/index.html
, um die Aufrufzahlen anzuzeigen.
<div class="post-list"> {% for post in posts %} <article class="post-item"> <h2><a href="/posts/{{ post.id }}">{{ post.title }}</a></h2> <p>{{ post.content[:150] }}...</p> <small>{{ post.createdAt.strftime('%Y-%m-%d') }} | Views: {{ post.view_count }}</small> </article> {% endfor %} </div>
Ausführen und Testen
Starten Sie Ihre Anwendung neu:
uvicorn main:app --reload
Öffnen Sie Ihren Browser und navigieren Sie zur Homepage Ihres Blogs.
In der Blogliste sehen Sie neben jedem Beitrag "Views: 0".
Klicken Sie, um die Detailseite eines beliebigen Artikels aufzurufen und aktualisieren Sie die Seite mehrmals. Sie werden feststellen, dass die Aufrufzahl für diesen Artikel entsprechend gestiegen ist.
Fazit
Sie haben nun erfolgreich ein Backend-System zur Verfolgung von Seitenaufrufen zu Ihrem FastAPI-Blog hinzugefügt. Ihre Benutzerbesuchsdaten liegen nun in Ihrer Hand.
Mit diesen Rohdaten können Sie tiefgehendere Datenoperationen und Analysen durchführen. Zum Beispiel:
- Deduplizierung: Zählen Sie mehrere Besuche derselben IP-Adresse innerhalb eines bestimmten Zeitfensters (z. B. eines Tages) als einen einzigen Besuch.
- Bots filtern: Identifizieren und filtern Sie Besuche von Suchmaschinen-Crawlern, indem Sie den
User-Agent
analysieren. - Daten-Dashboard: Erstellen Sie eine private Seite, um Trends bei den Artikelaufrufen mit Diagrammen zu visualisieren.
Die Daten liegen in Ihrer Hand, daher überlassen wir Ihnen diese Erkundungen.
Wenn Ihr Blog auf Leapcell bereitgestellt wird, hat Leapcell seine Web-Analysefunktion bereits automatisch für Sie aktiviert (dies ist komplett kostenlos).
Die Web-Analyse von Leapcell umfasst viele nützliche und leistungsstarke Funktionen zur Besucheranalyse. Mit ihr können Sie grundlegende Analysen des Besucher-Verhaltens einfach durchführen, ohne die mühsame Entwicklungsarbeit.