Seitenleiste: Eintraege ausblendbar (Backend)

Zwei Ebenen in einer Tabelle, nach dem Vorbild von DashboardLayout:
user_id NULL ist die Vorgabe des Administrators, eine Zeile mit user_id
die zusaetzliche Auswahl eines Kontos. Beide Listen gelten zusammen - wer
etwas versteckt, versteckt es zusaetzlich; einblenden, was der
Administrator ausgeblendet hat, kann niemand. Das erspart einen dritten
Zustand je Eintrag.

Als Schluessel dient der Routenpfad; der ist ohnehin eindeutig, eine
zweite Benennung waere nur eine weitere Stelle zum Auseinanderlaufen.

Drei Routen: lesen (Vorgabe, eigene Auswahl und die fertige Vereinigung
in einem Zug), eigene Auswahl setzen, Vorgabe setzen (nur Admin).

Die Eindeutigkeit laeuft ueber einen Index auf coalesce(user_id, -1).
Ein schlichtes UNIQUE(user_id) haette zwei Vorgabezeilen zugelassen, weil
NULL in SQL nie gleich NULL ist - das war im Entwurf noch falsch.

Versteckt wird nur der Eintrag, nicht die Seite: Lesezeichen,
Dashboard-Karten und Lagerort-QR sollen weiter funktionieren.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-28 20:27:14 +01:00
co-authored by Claude Opus 5
parent 8f5d679d9d
commit be267e70a5
5 changed files with 305 additions and 0 deletions
+43
View File
@@ -773,6 +773,49 @@ class DashboardLayout(Base):
)
class NavigationSichtbarkeit(Base):
"""Welche Eintraege der Seitenleiste jemand ausgeblendet hat.
``user_id = NULL`` ist die Vorgabe des Administrators: Was dort steht,
bekommt niemand zu sehen. Eine Zeile mit ``user_id`` ist die zusaetzliche
Auswahl eines Kontos.
**Beide Listen gelten zusammen.** Wer etwas versteckt, versteckt es
zusaetzlich; einblenden, was der Administrator ausgeblendet hat, kann
niemand. Das erspart einen dritten Zustand je Eintrag ("erben / zeigen /
verstecken") und laesst sich in einem Satz erklaeren.
Versteckt wird nur der EINTRAG, nicht die Seite: Lesezeichen,
Dashboard-Karten und Lagerort-QR verweisen weiter auf Routen, die hier
abgewaehlt sein koennen.
Vorbild ist :class:`DashboardLayout`: eigene Tabelle statt :class:`Setting`
(dessen ``value`` fasst 255 Zeichen und haelt genau einen Wert je
Schluessel), Nutzlast als JSON-Text. Als Schluessel dient der Routenpfad -
der ist ohnehin schon eindeutig, eine zweite Benennung waere nur eine
weitere Stelle, die auseinanderlaufen kann.
"""
__tablename__ = "navigation_sichtbarkeit"
__table_args__ = (
# Eine Zeile je Konto UND genau eine Vorgabezeile - beides mit einem
# Index. Ueber coalesce, weil NULL in SQL nie gleich NULL ist: ein
# schlichtes UNIQUE(user_id) liesse beliebig viele Vorgabezeilen zu.
Index("uq_navigation_zeile", text("coalesce(user_id, -1)"), unique=True),
)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
user_id: Mapped[int | None] = mapped_column(
ForeignKey("users.id", ondelete="CASCADE"), nullable=True
)
# JSON-Liste von Routenpfaden, z.B. ["/rezepte", "/shopping"].
versteckt: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
# Loescht man ein Konto, geht seine Zeile mit (ON DELETE CASCADE oben).
# In den Tests greift das nicht: SQLite erzwingt Fremdschluessel nur mit
# PRAGMA foreign_keys=ON, und das setzt die Testumgebung nicht.
class EtikettenVorlage(Base):
"""Eine gespeicherte Zusammenstellung fuer den Etikettenexport.