Ein perfekter Blog mit FastAPI: Tags für Beiträge
Ethan Miller
Product Engineer · Leapcell

Im vorherigen Artikel haben wir die Besucherzählung zu unserem Blog hinzugefügt, wodurch wir die Popularität jedes Beitrags visuell verfolgen können.
Der Blog wirkt ziemlich vollständig, aber es scheint, als ob noch etwas fehlt. Dein Blog hat bereits viele Beiträge, und die Benutzer könnten sich darin verlieren... Wie können Benutzer also schnell die Themen finden, die sie interessieren?
Genau, der Blog benötigt jetzt eine Tag-Funktion.
Tags sind eine klassische Methode, um Inhalte zu organisieren und zu kategorisieren. Indem jedem Beitrag Schlüsselwörter zugewiesen werden (z. B. "FastAPI", "Python", "Datenbank"), können Leser leicht alle Artikel finden, die sich auf ein bestimmtes Thema beziehen.
In den nächsten beiden Tutorials werden wir unserem Blogsystem eine vollständige Tagging-Funktion hinzufügen. In diesem Tutorial werden wir zuerst den grundlegenden Teil implementieren: die Unterstützung der Festlegung von Tags beim Erstellen eines Beitrags und deren Anzeige auf der Beitragsseite.
Schritt 1: Erstellen des Datenmodells für Tags
Um die Tag-Funktion zu implementieren, benötigen wir ein neues Tag
-Modell und müssen eine Beziehung zwischen Post
und Tag
herstellen. Ein Beitrag kann mehrere Tags haben, und ein Tag kann mit mehreren Beiträgen verknüpft sein. Dies ist eine typische Many-to-Many-Beziehung.
In SQLModel (und den meisten ORMs) erfordert die Implementierung einer Many-to-Many-Beziehung eine zusätzliche "Link-Tabelle", um die Verknüpfungsbeziehung zwischen Post
und Tag
zu speichern.
1. Aktualisieren der Modelldatei
Öffnen Sie die Datei models.py
, fügen Sie die Modelle Tag
und PostTagLink
hinzu und aktualisieren Sie das Modell Post
.
# models.py import uuid from datetime import datetime from typing import Optional, List from sqlmodel import Field, SQLModel, Relationship # ... User, PageView, Comment Klassen ... class PostTagLink(SQLModel, table=True): post_id: Optional[uuid.UUID] = Field( default=None, foreign_key="post.id", primary_key=True ) tag_id: Optional[uuid.UUID] = Field( default=None, foreign_key="tag.id", primary_key=True ) class Tag(SQLModel, table=True): id: Optional[uuid.UUID] = Field(default_factory=uuid.uuid4, primary_key=True) name: str = Field(unique=True, index=True) posts: List["Post"] = Relationship(back_populates="tags", link_model=PostTagLink) 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") page_views: List["PageView"] = Relationship(back_populates="post") # Fügen Sie die Many-to-Many-Beziehung mit Tag hinzu tags: List["Tag"] = Relationship(back_populates="posts", link_model=PostTagLink)
Code-Erklärung:
- Wir haben das
Tag
-Modell erstellt, das ein eindeutigesname
-Feld hat. - Wir haben das
PostTagLink
-Modell erstellt, das nur zwei Felder enthält:post_id
undtag_id
. Diese beiden Felder dienen als Fremdschlüssel, die auf die Tabellenpost
bzw.tag
verweisen, und bilden zusammen den Primärschlüssel. - In den Modellen
Post
undTag
haben wir jeweils einRelationship
-Feld (tags
undposts
) hinzugefügt. Der Schlüssel ist der Parameterlink_model=PostTagLink
, der SQLModel mitteilt, dass die Beziehung zwischen diesen beiden Modellen über die zwischengeschaltete TabellePostTagLink
aufrechterhalten wird.
2. Erstellen der Datenbanktabellen
Da wir zwei neue Modelle hinzugefügt haben, müssen wir die entsprechenden Tabellen in der Datenbank erstellen. Führen Sie die folgenden SQL-Anweisungen in Ihrer PostgreSQL-Datenbank aus:
-- Erstellen Sie die Tabelle tag CREATE TABLE "tag" ( "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), "name" VARCHAR UNIQUE NOT NULL ); -- Erstellen Sie die Link-Tabelle post_tag_link CREATE TABLE "posttaglink" ( "post_id" UUID REFERENCES "post"("id") ON DELETE CASCADE, "tag_id" UUID REFERENCES "tag"("id") ON DELETE CASCADE, PRIMARY KEY ("post_id", "tag_id") );
Hinweis: SQLModel wird PostTagLink
automatisch der Tabelle posttaglink
zuordnen. Stellen Sie sicher, dass der Tabellenname korrekt ist.
Wenn Ihre Datenbank auf Leapcell erstellt wurde,
können Sie SQL-Anweisungen einfach über die grafische Benutzeroberfläche ausführen. Gehen Sie einfach auf die Seite Datenbankverwaltung auf der Website, fügen Sie die obigen Anweisungen in die SQL-Oberfläche ein und führen Sie sie aus.
Schritt 2: Implementierung der Geschäftslogik für Tags
Um den Code sauber zu halten, erstellen wir eine neue Service-Datei für die Tag-Funktionalität.
Erstellen Sie eine neue Datei tags_service.py
im Stammverzeichnis des Projekts.
# tags_service.py import uuid from typing import List from sqlmodel import Session, select from models import Tag def find_or_create_tags(tag_names: List[str], session: Session) -> List[Tag]: """ Findet oder erstellt Tag-Entitäten basierend auf einer Liste von Tag-Namen. """ tags = [] if not tag_names: return tags # Abfrage nach vorhandenen Tags statement = select(Tag).where(Tag.name.in_(tag_names)) existing_tags = session.exec(statement).all() tags.extend(existing_tags) existing_tag_names = {tag.name for tag in existing_tags} # Ermitteln, welche Tags erstellt werden müssen new_tag_names = [name for name in tag_names if name not in existing_tag_names] # Neue Tags erstellen for name in new_tag_names: new_tag = Tag(name=name) session.add(new_tag) tags.append(new_tag) # Für Effizienz einmal committen session.commit() # Neu erstellte Tags aktualisieren, um ihre IDs zu erhalten for tag in tags: if tag.id is None: session.refresh(tag) return tags
Diese Funktion find_or_create_tags
nimmt eine Liste von Tag-Namen entgegen, fragt die Datenbank ab, gibt die Entitäten vorhandener Tags zurück und erstellt neue Einträge für nicht vorhandene Tags.
Schritt 3: Integration der Tag-Logik in die Beitragsrouten
Nun müssen wir die Route zum Erstellen von Beiträgen ändern, um Tag-Daten zu akzeptieren und zu verarbeiten.
Öffnen Sie routers/posts.py
, importieren Sie den neuen Service und aktualisieren Sie die Route create_post
.
# routers/posts.py # ... weitere Imports import tags_service # Importieren Sie den Tag-Service # ... @router.post("/posts", response_class=HTMLResponse) def create_post( title: str = Form(...), content: str = Form(...), tags: str = Form(""), # Fügen Sie neues Tags-Formularfeld hinzu session: Session = Depends(get_session), user: dict = Depends(login_required) ): # 1. Erstellen eines Post-Objekts new_post = Post(title=title, content=content) # 2. Verarbeiten von Tags if tags: # Parsen des kommagetrennten Strings in eine Liste von Tag-Namen tag_names = [name.strip() for name in tags.split(',') if name.strip()] # Tag-Entitäten finden oder erstellen tag_objects = tags_service.find_or_create_tags(tag_names, session) # Tags dem Beitrag zuordnen new_post.tags = tag_objects # 3. Speichern des Beitrags session.add(new_post) session.commit() return RedirectResponse(url="/posts", status_code=302) # ...
Wir haben der Funktion create_post
einen tags
-Parameter hinzugefügt, der einen kommagetrennten String von Tags vom Formular empfängt. Wir parsen diesen String, rufen tags_service
auf, um eine Liste von Tag
-Objekten zu erhalten, und weisen sie new_post.tags
zu. Dank des Relationship
von SQLModel kümmert sich SQLModel beim Committen von new_post
automatisch um die Einträge in der Link-Tabelle posttaglink
.
Schritt 4: Frontend-Anzeige
Der letzte Schritt ist die Änderung der Vorlagendateien, damit Benutzer Tags eingeben und sie auf der Beitragsseite sehen können.
Neue Beitragsseite
Öffnen Sie templates/new-post.html
und fügen Sie unterhalb des Inhaltsbereichs ein Eingabefeld für Tags hinzu.
{% include "_header.html" %} <form action="/posts" method="POST" class="post-form"> <div class="form-group"> <label for="content">Inhalt</label> <textarea id="content" name="content" rows="10" required></textarea> </div> <div class="form-group"> <label for="tags">Tags (kommagetrennt)</label> <input type="text" id="tags" name="tags" placeholder="z.B. fastapi, python, tutorial" /> </div> <button type="submit">Senden</button> </form> {% include "_footer.html" %}
Detailseite des Beitrags
Öffnen Sie templates/post.html
und fügen Sie unterhalb des Beitragsinhalts einen Bereich hinzu, um die Liste der Tags anzuzeigen.
<article class="post-detail"> <h1>{{ post.title }}</h1> <small>{{ post.createdAt.strftime('%Y-%m-%d') }} | Aufrufe: {{ view_count }}</small> <div class="post-content">{{ post.content | safe }}</div> {% if post.tags %} <div class="tags-section"> <strong>Tags:</strong> {% for tag in post.tags %} <a href="/tags/{{ tag.id }}" class="tag-item">{{ tag.name }}</a> {% endfor %} </div> {% endif %} </article>
Wir verwenden {% if post.tags %}
, um zu prüfen, ob der Beitrag Tags hat. Wenn ja, iterieren wir durch die Liste post.tags
und rendern jeden Tag als Link. Dieser Link ist noch nicht anklickbar; das werden wir im nächsten Artikel implementieren.
Ausführen und Testen
Starten Sie Ihre Anwendung neu:
Bây uvicorn main:app --reload
Nach dem Einloggen gehen Sie zur Seite "Neuer Beitrag". Sie sehen das neue Eingabefeld für Tags.
Geben Sie einige Tags getrennt durch Kommas ein, z. B. Python, Tutorial
, und senden Sie sie ab.
Nach dem Absenden gehen Sie zur Detailseite des Beitrags und Sie sehen, dass die Tags des Beitrags erfolgreich angezeigt werden.
Ihr Blog unterstützt nun die Erstellung und Anzeige von Tags. Benutzer können Artikel jedoch noch nicht nach Tags filtern. Diesen Teil werden wir im nächsten Tutorial implementieren.