DokuEinheit 2: Daten
Erstellen Sie eine Notizen-App mit Nexy, Teil 9: Paginieren Sie die Notizen
Teil 9 zeigt jeweils fünf Notizen mit Links zur nächsten und vorherigen Seite, gelesen aus dem page der URL: /notes?page=2.
Bevor Sie beginnen: die App von Teil 8.
Bitten Sie die Datenbank um eine Seite
OFFSET überspringt die Notizen der vorherigen Seiten, LIMIT behält fünf. Wenn Sie nach mehr als fünf fragen, erfahren Sie kostenlos, ob eine nächste Seite existiert:
from sqlmodel import Session, select from src.configs.db import enginefrom src.models.note_model import Note def all_notes() -> list[Note]: with Session(engine) as session: return list(session.exec(select(Note).order_by(Note.id))) def find(note_id: int) -> Note | None: with Session(engine) as session: return session.get(Note, note_id) def add_note(text: str) -> Note: note = Note(text=text) with Session(engine) as session: session.add(note) session.commit() session.refresh(note) return note def change_note(note_id: int, text: str) -> bool: with Session(engine) as session: note = session.get(Note, note_id) if note is None: return False note.text = text session.commit() return True def remove_note(note_id: int) -> bool: with Session(engine) as session: note = session.get(Note, note_id) if note is None: return False session.delete(note) session.commit() return True PAGE_SIZE = 5 def notes_page(number: int) -> tuple[list[Note], bool]: """One page of notes, oldest first, and whether a page comes after it.""" with Session(engine) as session: query = select(Note).order_by(Note.id).offset((number - 1) * PAGE_SIZE).limit(PAGE_SIZE + 1) found = list(session.exec(query)) return found[:PAGE_SIZE], len(found) > PAGE_SIZEfrom sqlmodel import Session, select from src.apps.notes.note_model import Notefrom src.configs.db import engine def all_notes() -> list[Note]: with Session(engine) as session: return list(session.exec(select(Note).order_by(Note.id))) def find(note_id: int) -> Note | None: with Session(engine) as session: return session.get(Note, note_id) def add_note(text: str) -> Note: note = Note(text=text) with Session(engine) as session: session.add(note) session.commit() session.refresh(note) return note def change_note(note_id: int, text: str) -> bool: with Session(engine) as session: note = session.get(Note, note_id) if note is None: return False note.text = text session.commit() return True def remove_note(note_id: int) -> bool: with Session(engine) as session: note = session.get(Note, note_id) if note is None: return False session.delete(note) session.commit() return True PAGE_SIZE = 5 def notes_page(number: int) -> tuple[list[Note], bool]: """One page of notes, oldest first, and whether a page comes after it.""" with Session(engine) as session: query = select(Note).order_by(Note.id).offset((number - 1) * PAGE_SIZE).limit(PAGE_SIZE + 1) found = list(session.exec(query)) return found[:PAGE_SIZE], len(found) > PAGE_SIZELesen Sie die Seitenzahl aus der URL ab
Das page der Abfragezeichenfolge ist vom Besucher gesendeter Text: Alles, was keine positive Zahl ist, fällt auf die erste Seite zurück, sodass /notes?page=abc immer noch antwortet.
from "@/components/NoteCard.nexy" import NoteCardfrom nexy import useSearchParamsfrom src.notes import notes_page params = useSearchParams()error = params.get("error", "")asked = params.get("page", "1")number = max(int(asked), 1) if asked.isdigit() else 1found = notes_page(number)notes = found[0]more = found[1]<section class="app-page"> <h1>Notes</h1> {% if error %} <p class="note-error" role="alert">{{ error | e }}</p> {% endif %} <form method="post" action="{{ __actions_path['create_note'] }}"> <input name="text" placeholder="A new note" maxlength="200" required> <button type="submit">Add</button> </form> {% for note in notes %} <NoteCard id="{{ note.id }}" text="{{ note.text }}" /> {% endfor %} <nav class="note-pages" aria-label="Pages of notes"> {% if number > 1 %}<a href="/notes?page={{ number - 1 }}" rel="prev">Newer</a>{% endif %} {% if more %}<a href="/notes?page={{ number + 1 }}" rel="next">Older</a>{% endif %} </nav></section>from "@/components/NoteCard.nexy" import NoteCardfrom nexy import useSearchParamsfrom src.notes import notes_page params = useSearchParams()error = params.get("error", "")asked = params.get("page", "1")number = max(int(asked), 1) if asked.isdigit() else 1found = notes_page(number)notes = found[0]more = found[1]<section class="app-page"> <h1>Notes</h1> {% if error %} <p class="note-error" role="alert">{{ error | e }}</p> {% endif %} <form method="post" action="{{ __actions_path['create_note'] }}"> <input name="text" placeholder="A new note" maxlength="200" required> <button type="submit">Add</button> </form> {% for note in notes %} <NoteCard id="{{ note.id }}" text="{{ note.text }}" /> {% endfor %} <nav class="note-pages" aria-label="Pages of notes"> {% if number > 1 %}<a href="/notes?page={{ number - 1 }}" rel="prev">Newer</a>{% endif %} {% if more %}<a href="/notes?page={{ number + 1 }}" rel="next">Older</a>{% endif %} </nav></section>rel="prev" und rel="next" teilen Browsern und Suchmaschinen mit, wie die Seiten aufeinander folgen, und aria-label benennt die Links für einen Screenreader.
Fügen Sie Notizen hinzu, bis mehr als fünf vorhanden sind: Die Liste endet bei fünf mit einem Link Älter. Es führt zu /notes?page=2, wo es einen Link zu Neuer gibt.
Was du gelernt hast
offsetundlimitlesen eine Seite; Wenn man nach einer Zeile mehr fragt, sagt man, ob eine weitere Seite folgt.useSearchParams()lautet?page=; Ein Wert des Besuchers wird vor seiner Verwendung überprüft.rel="prev"undrel="next"verlinken die Seiten für Browser, Crawler und Screenreader.
Nächste Schritte
- Einheit 3, Teil 10: Konto erstellen und anmelden
- SQLite: Indizes, Pragmas und Tests für eine In-Memory-Datenbank