## 1) Problemstellung: „Warum kommen mir so viele unnütze Ergebnisse entgegen?“ {#sec-b345eb02ca5e} Als die Anzahl der Beiträge in meinem Blog wuchs, wurde eine strukturierte Verwaltung unverzichtbar. Da mein Blog mehrsprachig ist, nutzen verschiedene Instanzen sogar dieselben Slugs, was das gezielte Auffinden einzelner Posts zunehmend erschwert. Das ist zwar ein Zeichen für wachsenden Content, aber die Suche wird knifflig. Das eigentliche Hindernis liegt im Standard‑Suchfeld von Django‑Admin (`search_fields`). Die Suchlogik ist **zu vage**. ![Ein Bibliothekar, der mit einer magischen Lupe ein Buch sucht](/media/editor_temp/6/12d4b1c2-0844-485d-b9ef-0e1f766b18ca.png) Wenn man viele Felder in `search_fields` definiert (und das passiert mit zunehmender Funktionalität sowieso), Telescope folgendes passiert, sobald man ein Wort eingibt: * Es trifft den Titel * Es trifft die Beschreibung * Es trifft den Benutzernamen * Es trifft sogar ein beliebiges Notizfeld Die Ergebnisse gehen von "zu viele" zu "meine gesuchte Information wird überdeckt" über. Gerade wenn man im hades Notfall einen bestimmten Domain‑Eintrag finden muss und das Ergebnis sich über Dutzende Einträge erstreckt, kann das ziemlich nervig werden. Daher kam die Idee: "Ich möchte ein einzelnes Feld gezielt durchsuchen können." --- ## 2) Lösungsansatz: `field:value`‑Syntax + `shlex` für Anführungszeichen {#sec-5381e00c4696} Die Idee ist simpel: * Suche mit **Feldangabe**, z. B. `name:diff` * Unterstütze **Suchbegriffe mit Leerzeichen**, z. B. `title:"system design"` Hier kommt `shlex.split()` ins Spiel. Das Admin‑Suchfeld teilt normalerweise einfach nach Leerzeichen, wodurch Eingaben wie `"system design"` zerstört werden. `shlex` respektiert Anführungszeichen ähnlich einer Shell und erzeugt korrekte Tokens. Beispiele: * `title:"system design"` → `["title:system design"]` * `name:diff python` → `["name:diff", "python"]` Damit lassen sich **feldspezifische Suchbegriffe** und **normale Suchbegriffe** elegant kombinieren. --- ## 3) Code‑Erklärung {#sec-8edf0b65ccd2} Der komplette Code liegt im Repository und kann direkt kopiert werden. [GitHub‑Repository] (https://github.com/mikihands/djangomixins/blob/main/admin_fieldscopedsearch.py) Im Blog beschränke ich mich auf die wichtigsten Punkte. ### `_build_lookup_for`: `search_fields`‑Präfixe unverändert übernehmen {#sec-25f0fe79cd1c} `search_fields` unterstützt Präfixe: * `=field` → exakte Übereinstimmung (case‑insensitive) * `^field` → Präfix‑Match * Standard → `icontains` Der Helper entscheidet anhand des Präfixes, welchen Lookup‑Typ er verwendet: ```python def _build_lookup_for(self, raw_field): if raw_field.startswith('='): return raw_field.lstrip('=^@'), '__iexact' if raw_field.startswith('^'): return raw_field.lstrip('=^@'), '__istartswith' return raw_field.lstrip('=^@'), '__icontains' ``` Setzt man in `admin.py` `search_fields = ['=name', 'title']`, gilt: * `name` → `iexact` * `title` → `icontains` Und genau dieselbe Lookup‑Logik wird auch bei `field:value` angewendet. --- ### `get_search_results`: Feldspezifisch = AND, normale Begriffe = wie gehabt {#sec-2432c8a5949d} Der Kern liegt in `get_search_results()`: 1. Tokenisierung mittels `shlex` 2. Aufteilung in zwei Gruppen * `field:value` – wenn das Feld in `search_fields` existiert → **field_specific_terms** * alles andere → **normal_terms** 3. Feldspezifische Begriffe werden **mit AND** kombiniert 4. Normale Begriffe folgen dem gewohnten Admin‑Verhalten (OR über alle Felder, AND zwischen den Begriffen) Wesentliche Ausschnitte: ```python terms = shlex.split(search_term or "") field_specs = [self._build_lookup_for(f) for f in getattr(self, 'search_fields', [])] field_lookup_map = {} for name, lookup in field_specs: field_lookup_map.setdefault(name, lookup) field_specific_terms = [] normal_terms = [] for t in terms: if ':' in t: field, value = t.split(':', 1) if field in field_lookup_map and value != '': field_specific_terms.append((field, value)) else: normal_terms.append(t) else: normal_terms.append(t) ``` Ungültige Feldnamen oder leere Werte werden einfach zu den normalen Suchbegriffen hinzugefügt. #### (1) `field:value` wird **AND**‑weise angerechnet ```python for field, value in field_specific_terms: lookup = field_lookup_map[field] qs = qs.filter(Q(**{f"{field}{lookup}": value})) ``` `name:diff status:active` wird zu `name==diff AND status==active`. #### (2) Normale Begriffe: (Feld OR) → Begriffe AND ```python if normal_terms and field_lookup_map: for term in normal_terms: term_q = Q() for field, lookup in field_lookup_map.items(): term_q |= Q(**{f"{field}{lookup}": term}) qs = qs.filter(term_q) ``` Beispiel: `name:diff python` * `name=diff` AND * `(name OR title OR description) contains python` Damit bleibt das vertraute Admin‑Suchgefühl erhalten, während `field:value` gezielt eingrenzt. --- ## 4) Einsatz: Einfach das Mixin einbinden {#sec-0eb3a92e04b6} ```python @admin.register(MyModel) class MyAdmin(FieldScopedSearchMixin, admin.ModelAdmin): search_fields = ['=name', 'title', 'description'] ``` Jetzt sind im Admin‑Suchfeld folgende Eingaben möglich: * `name:diff` * `title:"system design"` * `name:diff python` Die Hilfetexte können Sie bei Bedarf über `get_changelist_instance()` anpassen. --- ## 5) Rückblick & Grenzen: "Praktisch, aber ich will mehr" {#sec-4fc456da292d} Das Mixin hat mir ermöglicht, die Suche exakt nach meinen Bedürfnissen zu steuern – ein echter Gewinn im täglichen Betrieb. Einschränkungen gibt es jedoch: * **Full‑Text‑Search (@‑Präfix)** wird nicht behandelt. Eine Anbindung an PostgreSQL‑Full‑Text‑Search wäre möglich, würde aber weitere Konfigurationen (Gewichtung, tsvector) erfordern. * Da das Vorgehen auf `icontains` basiert, können bei großen Datenmengen **Performance‑Probleme** auftreten, besonders wenn viele Felder per OR abgefragt werden. * Zukünftige Ideen: * Datumsbereichssuche, z. B. `created_at:2026-01-01..2026-01-31` * Negativbedingungen, z. B. `status:active,-status:deleted` * Einfache Vergleichsoperatoren, z. B. `field>10` Für den Moment ist das Mixin jedoch klein, präzise und erhöht die Produktivität merklich – genau das, was ich im laufenden Betrieb brauchte. **Ein bisschen extra Aufwand für Idee und Umsetzung kann die Arbeitsabläufe dauerhaft verbessern – das ist doch das Schöne am Coden.** --- **Verwandte Beiträge** - [Warum man das Admin‑Interface sofort verstecken sollte](/ko/whitedec/2025/11/10/admin-now-need-to-hide/)