Was passiert hier?
Tagebuch
Entwicklungs-Tagebuch
Wie das alles begann
Ein Hobbykoch, keine Programmierkenntnisse, eine Idee. Hier ist der ehrliche Bericht was passiert ist.
Jetzt gerade
Was als Nächstes kommt
Entweder der erste eigene Blogbeitrag oder Login und Nutzerkonten.
Juli 2026 · Session 16
Aufwand und Schwierigkeit sind nicht dasselbe
Nach dem großen Foto-Batch aus Session 15 diesmal der Gegenpol: ein einzelnes neues Rezept von Hand einpflegen, Haschee mit Rinderhack und Guinness-Sauce. Dabei fiel auf, dass zwei Datenfelder in der Rezepttabelle bisher komplett leer waren — Zeit und Aufwand wurden beim ursprünglichen Import nie befüllt.
Beim genaueren Hinsehen zeigte sich: "Aufwand" (wie viel Arbeit steckt drin) und "Schwierigkeit" (wie anspruchsvoll ist es) sind eigentlich zwei verschiedene Dinge — ein Rezept kann viele einfache Handgriffe haben (hoher Aufwand, geringe Schwierigkeit) oder einen einzigen kniffligen Schritt (umgekehrt). Beide Felder jetzt sauber getrennt in der Datenbank. Genauso bei der Zeit: statt einer einzigen "Zubereitungszeit" gibt es jetzt aktive Kochzeit (Schnippeln, Anbraten, Rühren) und passive Kochzeit (Schmoren ohne Zutun) getrennt, die Gesamtzeit wird daraus automatisch addiert. Für die Anzeige in der Rezeptübersicht bewusst gegen Ampelfarben entschieden: Zwei grün-gelb-rote Punkte nebeneinander für zwei verschiedene Skalen wären schwerer auseinanderzuhalten gewesen als einfacher Text.
Ein Word-Dokument hat noch eine kleine Überraschung geliefert: Zeit- und Schwierigkeitsangaben steckten ursprünglich in einer Textbox in der Kopfzeile — für das übliche Python-Werkzeug zum Auslesen von Word-Dateien unsichtbar, weil Textboxen technisch anders gespeichert werden als normaler Text. Erst der Blick ins rohe XML hinter der .docx-Datei brachte die Werte zutage. Einfacher war es, die Angaben stattdessen als normale Textzeilen ins Dokument zu schreiben — das Import-Skript liest sie jetzt per Mustererkennung (Regex) automatisch heraus, für jedes künftige Rezept.
Der Weg auf die Live-Seite ging diesmal ohne den großen Massenimport: eine einzelne SQL-Datei mit allen Änderungen, von Hand in die Render-Datenbank eingespielt. Dabei ein eigener Fehler beim Testen aufgefallen — die neue Kategorie hieß "07_Hackfleisch" statt wie überall sonst "07 - Hackfleisch", wodurch die Anzeige nur noch "ckfleisch" zeigte. Ohne den Blick auf die echte Live-Seite wäre das nicht aufgefallen.
Juli 2026 · Session 15
199 Rezeptfotos und 86 Platzhalter - von der Word-Datei bis Cloudinary
Der große Brocken von der "Was noch kommt"-Liste: echte Rezeptfotos statt Kochlöffel-Emoji. Ein Python-Skript zieht das erste eingebettete Bild aus jeder Word-Datei (Word-Dokumente sind intern nichts anderes als ZIP-Archive — word/media/image1.jpeg ist das Rezeptfoto, image2 meist das Logo). 285 von 286 Dateien lieferten ein Bild, eine einzige mit einer leeren Struktur wurde aussortiert.
Die Zuordnung zur Datenbank lief überraschend reibungslos: 100% Treffer, weil der Rezepttitel exakt nach demselben Verfahren aus dem Word-Dokument gelesen wurde wie beim ursprünglichen Import in Session 5 — gleiche Quelle, gleiche Methode, kein Rätselraten nötig. Die Bilder selbst wanderten zu Cloudinary, die Datenbank bekam nur die URL (foto_url), lokal liegt danach nichts mehr — der Extraktionsordner war nur eine Zwischenstation.
Ein Fund unterwegs: Bei 86 Rezepten enthielt die Word-Datei gar kein eigenes Foto — stattdessen wurde versehentlich das Logo als "Rezeptfoto" mitgezogen. Erkannt per Bild-Hash-Vergleich: Ein echtes Foto ist einzigartig, das Logo taucht identisch in vielen Dateien auf. Alle 86 Duplikate bei Cloudinary gelöscht und durch einen einzigen gemeinsamen Platzhalter ("Das richtige Foto folgt bald!") ersetzt, statt 86-mal dieselbe Datei mitzuschleppen.
Zum Schluss aufgeräumt: Der lokale Bilderordner gelöscht (nicht mehr gebraucht, alles liegt bei Cloudinary), der Word-Importordner von alten Dateien geleert — die 21 Kategorie-Unterordner bleiben aber stehen und werden ab jetzt zur Ablage für neue Rezepte.
Juli 2026 · Session 14
Ein kleiner Bugfix und eine lange Spurensuche
Angefangen hat es mit einer Kleinigkeit: ein fehlendes Hintergrundbild auf der Startseite, das im Browser als 404 auftauchte. Der schnelle Fix — Logo entfernen, totes Template löschen — war in fünf Minuten erledigt. Aber eine Nachfrage ("Gibt es noch mehr alte Strukturen?") öffnete eine viel größere Baustelle.
Zuerst ein verschachteltes <main>-Tag im Tagebuch: `base.html` wickelt den ganzen Seiteninhalt schon in ein `<main>`, aber gleich drei Templates (Tagebuch, Impressum, Datenschutz) hatten zusätzlich noch ein eigenes <main> darin — ungültiges HTML, und eine CSS-Regel auf dem bloßen Tag zwang dadurch die ganze Seite in eine schmale Spalte. Dieselbe Fehlerkategorie fand sich noch vier weitere Male: rohe a/p-Selektoren auf Impressum/Datenschutz ließen sogar die Navigation im Header unterstrichen aussehen, und die Hermann-Seite schleppte noch eine komplette Kopie von :root, body, Nav und Footer aus ihrer alten Standalone-HTML-Zeit mit sich herum — toter Code, der nur zufällig nicht auffiel.
Am subtilsten: Eine Klasse plus ein rohes Tag im selben Container (z. B. .news-card p) ist spezifischer als eine einzelne Klasse (.news-card-date) — und gewinnt automatisch, auch wenn das nie beabsichtigt war. Fünfmal fiel dieses Muster auf: Ein Eyebrow-Text in Grau statt Burgunder, eine Signatur in normaler statt kursiver Schrift, ein Datums-Label ohne Abstand, ein Kochabend-Status in der falschen Farbe. Alles unsichtbare Bugs, bis genau hingeschaut wurde.
Am Ende alle Textseiten auf eine einheitliche Breite (860px) gebracht, Impressum und Datenschutz komplett auf die dunkle Kopfleiste umgestellt (eine frühere Entscheidung, dass sie anders aussehen dürfen, wurde bewusst zurückgenommen) — und der "Was noch kommt"-Block im Tagebuch mit der echten, aktuellen Liste befüllt: zehn Kacheln, sortiert nach Freemium-Logik (Login ganz ans Ende, damit er nicht wie eine Hürde wirkt, sondern die spannenden Features vorne stehen).
Juli 2026 · Session 13
Die Startseite bekommt echte Daten
Die startseite.html lag schon länger fertig gestaltet im Projekt — ein Magazin-Layout mit Blöcken für Neuigkeiten, neue Rezepte, Rezept der Woche und Kochabende. Nur: Die Routen lieferten überall leere Listen, und die Feldnamen im Template (titel, kategorie.name) passten gar nicht zum echten Recipe-Modell (title, category). Ein Template, das gegen die Zielstruktur aus dem Konzept geschrieben war, nicht gegen die aktuelle Wirklichkeit.
Bevor Code geschrieben wurde, erstmal eine Grundsatzfrage geklärt: Was ist eigentlich der Unterschied zwischen der Landing-Page (/) und dieser Startseite (/startseite)? Ergebnis: Beide bleiben dauerhaft bestehen, mit klar getrennten Aufgaben — / bleibt die persönliche Begrüßung mit Hermann und Baufortschritt, /startseite wird der eigentliche Magazin-Hub. Falls sie später einen eigenen Nav-Punkt bekommt, dann nicht "Startseite" genannt — das würde mit der echten Homepage verwechselt.
Für den Block "Neue Rezepte" musste die Datenbank um ein Feld erstellt_am erweitert werden, damit "neueste zuerst" überhaupt eine Bedeutung hat. Diesmal wurde die Lektion aus Session 11 richtig angewendet: Produktionsdatenbank vor dem Push migriert, nicht danach. Kein Crash. Am Ende: ein Rotationssystem, das dem "Neuigkeiten"-Block optisch gleicht — eine große Karte, zwei kleinere daneben, gleiche Kartenhöhe.
Und dann, ausgerechnet beim Tagebucheintrag selbst: vergessen. Nach dem Push direkt zur Zusammenfassung gesprungen, ohne diesen Eintrag zu schreiben. Wird hiermit nachgeholt.
Juni 2026 · Session 12
Über mich — eine Seite, die schon lange in der Nav wartete
Die Navigation zeigte schon seit Wochen auf /ueber-mich — nur gab es dahinter weder Route noch Template. Heute wurde das nachgeholt: eine neue Flask-Route, ein neues Jinja2-Template, aufgebaut nach dem inzwischen vertrauten Muster (dunkler Header mit Eyebrow und Titel, heller Textbereich darunter).
Der Text stand schon fertig bereit — ehrlich, persönlich, ohne Marketing-Floskeln. Passend dazu ein großformatiges Küchenfoto quer über die volle Seitenbreite, mit einer kleinen, bewusst sichtbaren Bildunterschrift: "Foto mit KI bearbeitet". Gerade weil die Seite von Ehrlichkeit spricht, sollte auch das Bild ehrlich gekennzeichnet sein.
Diesmal ohne Umwege: Route und Template folgen einem Muster, das inzwischen mehrfach entstanden ist (Rezeptseite, Kochmodus, Druckansicht) — neue Seiten baut man jetzt spürbar schneller als noch vor ein paar Sessions.
Gleich hinterher die nächste Baustelle geschlossen: Der Shop-Link in der Navigation führte bislang komplett ins Leere. Jetzt gibt es eine augenzwinkernde "Kommt bald"-Seite — mit Hermann, der entschuldigend die Schultern zuckt. Passend dazu ein neues Bild, hermann_sorry.png, geprüft mit demselben Python-Alpha-Kanal-Check aus Session 9. Diesmal mit einer wichtigen Lektion: Nicht nur die vier Bildecken reichen als Beweis für saubere Freistellung. Eine Voransicht zeigte einen unscharfen Farbschleier um Hermann — der stellte sich aber als reines Anzeigeverhalten des Vorschau-Tools heraus (grauer Verlauf statt Schachbrettmuster für transparente Bereiche). Eine echte Alpha-Statistik über das ganze Bild und ein Test auf hellem und dunklem Hintergrund schafften Klarheit: sauber freigestellt.
Juni 2026 · Session 11
Drucken — und ein Deploy, der erst mal schiefging
Das Datenbank-Konzept sieht zwei Druckvarianten pro Rezept vor: "Nur Rezept" (einfach, für die meisten der 285 bestehenden Rezepte) und "Kochbuchseite" (mit Einleitung, Aroma-Box, Profitipp — kommt erst wenn Rezepte entsprechend ergänzt sind). Beide Buttons stehen jetzt im Rezept-Header, die Kochbuchseite bleibt ausgegraut bis genug Felder befüllt sind.
Die Druckseite ist bewusst kein normales Template — sie erweitert `base.html` nicht, hat keine Navigation und keinen Footer, dafür ein eigenständiges, druckoptimiertes Layout mit automatischem Druckdialog beim Öffnen. Kleiner Wermutstropfen: Chrome zeigt beim Drucken standardmäßig Datum und Seitentitel in Kopf- und Fußzeile an — das lässt sich per CSS nicht abstellen, nur manuell in den Druckeinstellungen.
Dann der Schreck: Nach dem Push zeigte kochmalwasanderes.de nur noch "500 Internal Server Error". Ursache war schnell gefunden — die neuen Datenbankspalten aus Session 10 (aufwand, zubereitungszeit, gesamtzeit) existierten nur in der lokalen Datenbank, nicht in der Produktionsdatenbank auf Render. SQLAlchemy fragt bei `Recipe.query.count()` intern alle Modellspalten ab, auch wenn nur gezählt wird — fehlt eine Spalte in der echten Datenbank, kracht es überall, nicht nur auf der Rezeptseite. Die fehlenden `ALTER TABLE`-Befehle einmal per psql direkt gegen die Produktionsdatenbank ausgeführt, Seite lief wieder.
Lektion: Lokale und Produktionsdatenbank sind zwei getrennte Welten. Ein `ALTER TABLE` lokal macht die Seite live nicht automatisch kaputt oder heil — es muss auf beiden Seiten passieren.
Juni 2026 · Session 10
Rezeptseiten und Kochmodus im neuen Gewand
Die Einzelseiten der Rezepte und der Kochmodus liefen noch komplett im alten Bootstrap-Stil aus Session 5 — pt-Einheiten, `d-flex`-Klassen, die seit dem Bootstrap-Abschied in Session 7 gar nichts mehr taten. Das fiel besonders am verrutschten Logo auf der Rezeptseite auf: Ohne Bootstrap hatte `justify-content-between` keine Wirkung mehr, das Logo landete einfach unter dem Titel statt daneben.
Beide Templates komplett neu aufgesetzt, im aktuellen Design-System: dunkler Header mit Kategorie, Titel und Meta-Zeile, saubere Zutatentabelle ohne Kasten-Optik. Der Kochmodus bekam einen großen, gut lesbaren Zutat-Text und die Zurück/Weiter-Buttons klar getrennt — Zurück links, Weiter rechts, wie es sich beim Arbeiten in der Küche mit vollen Händen anfühlt.
Nebenbei: Alle Eyebrow-Labels (die kleinen Kategorie-Überschriften) von 0.72rem auf 0.82rem vergrößert — auf allen Seiten einheitlich. Und die Datenbank um drei optionale Felder erweitert: Aufwand, Zubereitungszeit, Gesamtzeit. Die meisten der 285 bestehenden Rezepte haben diese Angaben noch nicht — die Felder erscheinen deshalb nur, wenn sie tatsächlich befüllt sind.
Juni 2026 · Session 9
Hermann ist da! 🎉
Endlich. Hermann steht auf der Landing-Page — verschränkte Arme, Kochschürze, KmwA-Logo auf der Brust. Aber bis er wirklich da war, gab es noch einige Hürden zu überwinden.
Die erste Lieferung der neuen Bilder hatte dasselbe Problem wie in Session 8: kein echter Alpha-Kanal, der Hintergrund als sichtbare Pixel eingebrannt. Mit einem kleinen Python-Einzeiler lässt sich das inzwischen sekundenschnell prüfen — Ecke oben links auslesen, Alpha-Wert checken: A=0 bedeutet transparent und korrekt freigestellt, A=255 bedeutet zurück zur Nachbearbeitung. Beim zweiten Anlauf stimmte es.
Dann die Frage: Wie kommt Hermann auf die Startseite? Zwei Optionen standen zur Wahl. Option A: Zwei-Spalten-Layout wie auf seiner eigenen Vorstellungsseite — Text links, Hermann rechts, klar und strukturiert. Option B: Hermann absolut positioniert im Hero, leicht abgeschnitten, Text bleibt zentriert — dynamischer, als würde er "reinschauen". Es wurde Option B. Ein bisschen Größe, ein bisschen nach links, und er steht genau richtig.
Nebenbei: Die Arm-Animation wurde vorerst entfernt. Das zweite Bild (der winkende Arm als separates Layer) war noch nicht korrekt freigestellt. Hermann bleibt also erst einmal statisch — was ihm bei verschränkten Armen ohnehin gut zu Gesicht steht.
Juni 2026 · Session 8
Als CSS gegen eine PNG-Datei verlor
Diese Session hat eine wichtige Lektion gelehrt: Man kann sich nicht mit CSS aus schlechten Bilddaten herausschrauben. Hermann — der KI-Kochkumpel — sollte auf dunklem Hintergrund erscheinen. Aber egal wie viele CSS-Zeilen wir schrieben, hinter ihm blieb ein weißlich-graues Schachbrettmuster.
Der Grund: Die PNG-Dateien hatten keinen echten Transparenz-Kanal (Alpha-Kanal). Normalerweise speichert ein PNG für jeden Pixel nicht nur Farbe, sondern auch einen vierten Wert: Alpha — von 0 (unsichtbar) bis 255 (vollständig sichtbar). Transparente Stellen sind dann wirklich leer. Unsere Bilder hatten das nicht. Der Hintergrund war als hellgraue Pixel eingebrannt, ununterscheidbar vom restlichen Bild.
Erster Reparaturversuch: Ein Python-Skript mit Flood-Fill — der Algorithmus startet an den Bildrändern, breitet sich aus und macht alle Pixel transparent, die ähnlich hell sind wie der Hintergrund. Klingt schlau. Ergebnis: Hermann hatte plötzlich ein Loch im Kochkittel und fehlende Hautpartien. Sein weißer Kittel war dem weißlichem Hintergrund zu ähnlich.
Zweiter Versuch: Sättigungs-Prüfung zusätzlich zur Helligkeit. Grau-Weiß hat kaum Farbton (Sättigung nahe 0), Hermanns Haut und Kittel schon etwas mehr. Das Skript lief sauber — aber der Hintergrund im Bild hatte teils dieselbe Sättigung wie sein Kittel. Kein zuverlässiges Ergebnis.
Fazit: Neue Bilder. Diesmal mit echtem Alpha-Kanal oder über ein KI-Tool wie remove.bg. Bis dahin läuft die Hermann-Seite mit den Original-Dateien — das Schachbrettmuster ist im Git-Log als TODO vermerkt. Manchmal ist "warten auf bessere Ausgangsdaten" die richtige Entscheidung.
Was nebenbei noch passiert ist: alle Seiten bekamen vollständige Routen, das Genussregister wurde komplett überarbeitet (Live-Suche, Akkordeon, Hover-Effekte), und die Landing-Page ist jetzt mit dem Tagebuch verknüpft.
Juni 2026 · Session 7
Neues Design — Lora, DM Sans und ein System
Weg von Bootstrap, hin zu einem eigenen Design-System. Zwei Schriften: Lora für Überschriften (Serife, warm, editorial), DM Sans für alles andere (schlank, klar). CSS-Variablen als zentrale Schaltstelle — Farben, Abstände und Schriften einmal definiert, überall verfügbar. Hermann-Vorstellungsseite von 7 MB Standalone-HTML auf 15 KB Jinja2-Template geschrumpft.
Juni 2026 · Session 6
Die Seite geht live ✨
Deployment auf Render.com: requirements.txt, gunicorn, GitHub-Repository, automatisches Deployment bei jedem Code-Push. 285 Rezepte aus der lokalen Datenbank nach Render migriert. Domain kochmalwasanderes.de bei IONOS per DNS auf Render umgeleitet.
Juni 2026 · Session 5
285 Rezepte — und ein Kochmodus
Ein Import-Skript liest alle Word-Dokumente aus und überträgt sie automatisch in die Datenbank. 246 Rezepte in 21 Kategorien, in einer Nacht. Dazu drei HTML-Templates mit Bootstrap: Übersicht, Einzelseite, Kochmodus. Der Kochmodus zeigt immer einen Schritt auf einmal — groß, gut lesbar, mit Fortschrittsbalken.
Juni 2026 · Session 4
Echte Tabellenbeziehungen
Ein Rezept hat viele Zutaten — das klingt simpel, ist aber ein wichtiges Datenbankkonzept. Statt Zutaten als langen Text zu speichern, bekommt jede Zutat eine eigene Zeile in einer eigenen Tabelle.
Juni 2026 · Session 3
Erste Daten, erste Route
Testrezepte per Hand in die Datenbank eingetragen, eine Route gebaut die sie im Browser anzeigt. Zum ersten Mal fließen echte Daten von PostgreSQL durch Python in den Browser.
Juni 2026 · Session 2
Flask trifft Datenbank
Erste Verbindung zwischen Python und PostgreSQL hergestellt. Eine Python-Klasse beschreibt eine Datenbanktabelle, SQLAlchemy übersetzt das ins richtige Format. Tabelle in pgAdmin sichtbar: kleiner Jubel.
Juni 2026 · Session 1
Der erste Schritt — und der erste Tippfehler
Am Anfang stand eine einfache Idee: eine Rezeptsammlung online stellen. Der erste eigene Code war eine einzige Zeile: "Hier entsteht eine Koch-Website" im Browser. Es hat drei Versuche gebraucht, weil aus app erst qpp wurde.