Schlagwortsuche und Paginierung
In diesem Kapitel lernst du, SQLAlchemy für die kombinierte Suche zu verwenden und die Listenseite mit der in Flask-SQLAlchemy integrierten Paginierung zu optimieren.
ilike — Suche ohne Beachtung der Groß-/Kleinschreibung
SQLAlchemy'silike-Methode implementiert ein Fuzzy-Matching ohne Beachtung der Groß-/Kleinschreibung.
Es entspricht dem SQL-LIKEOperator, ignoriert jedoch die Groß-/Kleinschreibung.
Beispiel
posts = Post.query.filter(Post.title.ilike('%flask%')).all()
# Kombinierte Suche über mehrere Felder: or_() kennzeichnet eine ODER-Beziehung
from sqlalchemy import or_
posts = Post.query.filter(
or_(
Post.title.ilike('%flask%'),
Post.summary.ilike('%flask%')
)
).all()
| Methoden | SQL-Operatoren | Groß-/Kleinschreibung beachten |
|---|---|---|
| like() | LIKE | Unterscheiden |
| ilike() | LIKE (Groß-/Kleinschreibung ignorieren) | Nicht unterscheiden |
| contains() | LIKE '%x%' | Unterscheiden |
| startswith() | LIKE 'x%' | Unterscheiden |
| in_() | IN | Exakte Übereinstimmung |
paginate — Paginierung
Flask-SQLAlchemy bietetpaginate()Die Methode gibt aktuelle Seitendaten + Paginierungsmetadaten in einem einzigen Aufruf zurück.
Beispiel
page = request.args.get('page', 1, type=int) # Aktuelle Seitenzahl (Standard: Seite 1)
per_page = 6 # 6 Artikel pro Seite anzeigen
# paginate(page, per_page, error_out)
# error_out=False: Wenn die Seitenzahl den Bereich überschreitet, wird statt 404 eine leere Liste zurückgegeben
pagination = posts_query.paginate(page=page, per_page=per_page, error_out=False)
posts = pagination.items # Artikelliste für die aktuelle Seite
Attribute des pagination-Objekts
| Eigenschaft | Type | Beschreibung |
|---|---|---|
| items | Liste | Daten der aktuellen Seite |
| page | int | Nummer der aktuellen Seite |
| pages | int | Gesamtzahl der Seiten |
| total | int | Gesamtzahl der Datensätze |
| has_prev | bool | Gibt an, ob es eine vorherige Seite gibt |
| has_next | bool | Gibt an, ob es eine nächste Seite gibt |
| prev_num | int | Nummer der vorherigen Seite |
| next_num | int | Nächste Seitennummer |
Vollständige Suche + Paginierungsansicht
Beispiel
from flask import Blueprint, render_template, request
from sqlalchemy import or_
from app.models import Post, Category
main_bp = Blueprint('main', __name__)
@main_bp.route("/")
def index():
category_slug = request.args.get('category', '')
keyword = request.args.get('q', '').strip()
page = request.args.get('page', 1, type=int)
per_page = 6
# Basisabfrage
posts_query = Post.query.order_by(Post.created_at.desc())
# Kategoriefilter
if category_slug:
posts_query = posts_query.join(Category).filter(Category.slug == category_slug)
# Stichwortsuche (Titel oder Zusammenfassung)
if keyword:
posts_query = posts_query.filter(
or_(
Post.title.ilike(f'%{keyword}%'),
Post.summary.ilike(f'%{keyword}%')
)
)
# Paginierung
pagination = posts_query.paginate(
page=page, per_page=per_page, error_out=False)
posts = pagination.items
return render_template('index.html',
posts=posts, pagination=pagination,
categories=Category.query.all(),
category_slug=category_slug, keyword=keyword)
Abfrageparameter werden über die URL übergeben (z. B.,
/?q=flask&page=2), die Navigationslinks der Paginierung müssen die aktuellen Such- und Kategorieparameter enthalten, andernfalls gehen die Bedingungen nach dem Blättern verloren.
Suchfeld und Paginierungsnavigation im Template
Beispiel
{% extends 'base.html' %}
{% block title %}EXAMPLE Blog - Startseite{% endblock %}
{% block content %}
<h2 class="section-title">Neueste Artikel</h2>
<!-- Suchfeld -->
<div class="search-bar">
<form method="get" action="{{ url_for('main.index') }}">
<input type="text" name="q" value="{{ keyword }}"
placeholder=Artikeltitel oder Zusammenfassungen durchsuchen... class="search-input">
{% if keyword %}
<a href="{{ url_for('main.index', category=category_slug) }}" class="clear-btn">✕</a>
{% endif %}
</Form>
</div>
<!-- Kategoriefilter -->
<div class="category-bar">
<a href="{{ url_for('main.index', q=keyword) }}"
class="{% if not category_slug %}active{% endif %}">Alle</a>
{% for cat in categories %}
<a href="{{ url_for('main.index', category=cat.slug, q=keyword) }}"
class="{% if category_slug == cat.slug %}active{% endif %}">
{{ cat.name }}
</a>
{% endfor %}
</div>
<p class="result-info">
Insgesamt {{ pagination.total }} Artikel
{% if keyword %}, Suche nach "{{ keyword }}"{% endif %}
</p>
{% if posts %}
<div class="article-grid">
{% for post in posts %}
<a href="{{ url_for('posts.post_detail', post_id=post.id) }}" class="card-link">
<div class="article-card">
<div class="card-content">
<span class="card-category">{{ post.category.name }}</span>
<h3>{{ post.title }}</h3>
<p>{{ post.summary|truncate(80) }}</p>
<span class="card-date">{{ post.created_at.strftime('%Y-%m-%d') }}</span>
</div>
</div>
</a>
{% endfor %}
</div>
<!-- Paginierungsnavigation -->
{% if pagination.pages > 1 %}
<div class="pagination">
{% if pagination.has_prev %}
<a href="{{ url_for('main.index', page=pagination.prev_num, category=category_slug, q=keyword) }}">← Vorherige Seite</a>
{% endif %}
{% for page_num in range(1, pagination.pages + 1) %}
{% if page_num == pagination.page %}
<span class="current">{{ page_num }}</span>
{% else %}
<a href="{{ url_for('main.index', page=page_num, category=category_slug, q=keyword) }}">{{ page_num }}</a>
{% endif %}
{% endfor %}
{% if pagination.has_next %}
<a href="{{ url_for('main.index', page=pagination.next_num, category=category_slug, q=keyword) }}">Nächste Seite →</a>
{% endif %}
</div>
{% endif %}
{% else %}
<p class="empty-tip">Keine passenden Artikel gefunden.</p>
{% endif %}
{% endblock %}
Kapitelzusammenfassung
In diesem Kapitel hast du die praktischen Funktionen der Listenseite gemeistert: die ilike-Suche ohne Berücksichtigung der Groß-/Kleinschreibung, die kombinierte Suche über mehrere Felder mit or_(), die paginate()-Abfrage zur Seitennummerierung und das Rendern der Paginierungsnavigation in Templates unter Beibehaltung der Filterparameter.
Suche + Kategoriefilterung + Paginierung lassen sich beliebig kombinieren, und die Bedingungen gehen nicht verloren.
Weitere Erweiterungen