Neues Formular für das Anlegen neuer Gerichte erstellt.

This commit is contained in:
2026-06-26 09:52:36 +02:00
parent 60a046939b
commit f42ca76066
4 changed files with 136 additions and 28 deletions
-1
View File
@@ -18,7 +18,6 @@ class GerichtForm(forms.ModelForm):
'ist_allergene_frei', 'ist_allergene_frei',
'allergene', 'allergene',
'beschreibung', # Neues Feld für Zutaten/Rohstoffe 'beschreibung', # Neues Feld für Zutaten/Rohstoffe
'bilder', # Das Bild-Feld
'ist_dauerangebot' 'ist_dauerangebot'
] ]
+105 -17
View File
@@ -1,32 +1,120 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de"> <html lang="de">
<head> <head>
<!-- (Standard Head und Bootstrap CDN einbinden) --> <meta charset="UTF-8">
<title>Neues Gericht hinzufügen</title>
<!-- Bootstrap CSS einbinden -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- OPTIONAL: Füge hier deinen eigenen, kleinen Custom-CSS-Link hinzu! -->
</head> </head>
<body class="bg-light">
<div class="container mt-5">
<h1>Neue Speiseart hinzufügen</h1>
{# !!! KRITISCH WICHTIG: enctype muss für Dateiuploads stehen !!! #} <body>
<form method="post" enctype="multipart/form-data"> <div class="container mt-5 mb-5">
<!-- Der Haupttitel muss prominent sein und signalisiert den Zweck der Seite -->
<header class="mb-4 border-bottom pb-2">
<h1>🥗 Neues Gericht Speiseplan hinzufügen</h1>
<p class="text-muted">Bitte fülle alle notwendigen Felder aus, um das Gericht zu veröffentlichen.</p>
</header>
<!-- Die Hauptkarte für das Formular -->
<div class="card shadow p-4 mb-5" id="gericht-form-container">
{# ⚠️ Wichtig: enctype bleibt erhalten! #}
<form method="post" enctype="multipart/upload">
{% csrf_token %} {% csrf_token %}
<!-- Der Standardway, alle Felder mit Bootstrap zu stylen (für Anfänger am besten) --> <!-- ==================== BLOCK 1: GRUNDDATEN (Kategorie, Name, Preis) ==================== -->
{# Man kann die komplexe Struktur durch eine einfache Schleife ersetzen #} <div class="card mb-4 shadow-sm border-primary">
{% for field in form %} <div class="card-header bg-primary text-white p-3">Grunddaten &amp; Identifikation</div>
<div class="mb-3"> <div class="card-body row g-3">
<label for="{{ field.id_for_label }}" class="form-label">{{ field.label }}</label>
{{ field }}
<!-- Fehlerhinweis des Feldes wird hier automatisch gerendert --> <!-- Name (col-md-4) -->
{% if field.errors %} <div class="col-lg-4 col-md-6 mb-3">
<p class="text-danger small mt-1">{{ field.errors }}</p> {{ form.name }}
{% if form.name.errors %}<p class="text-danger small mt-1">{{ form.name.errors }}</p>{% endif %}
</div>
<!-- Kategorie (Dropdown) -->
<div class="col-lg-4 col-md-6 mb-3">
{{ form.kategorie }}
{% if form.kategorie.errors %}<p class="text-danger small mt-1">{{ form.kategorie.errors }}</p>{% endif %}
</div>
<!-- Preis (Money Input) -->
<div class="col-lg-4 col-md-6 mb-3">
{{ form.preis }}
{% if form.preis.errors %}<p class="text-danger small mt-1">{{ form.preis.errors }}</p>{% endif %}
</div>
</div>
</div>
<!-- ==================== BLOCK 2: DETAILS & STATUS (Allergene, Flags) ==================== -->
<div class="card mb-4 shadow-sm border-warning">
<div class="card-header bg-warning text-dark p-3">Details und Statusprüfung</div>
<div class="card-body row g-3">
<!-- Switches (Checkboxen) - Nehmen wenig Platz ein -->
<div class="col-md-4 col-lg-2 mb-3">
{% if form.ist_vegetarisch %}
{{ form.ist_vegetarisch }} <label for="{{ form.ist_vegetarisch.id_for_label }}" class="form-check-label">Vegetarisch</label>
{% endif %} {% endif %}
</div> </div>
{% endfor %}
<button type="submit" class="btn btn-success mt-4">Gericht Speichern & Veröffentlichen</button> <div class="col-md-4 col-lg-2 mb-3">
{% if form.ist_allergene_frei %}
{{ form.ist_allergene_frei }} <label for="{{ form.ist_allergene_frei.id_for_label }}" class="form-check-label">Allergenfrei</label>
{% endif %}
</div>
<!-- Dauerangebot -->
<div class="col-md-4 col-lg-2 mb-3">
{{ form.ist_dauerangebot }}
<label for="{{ form.ist_dauerangebot.id_for_label }}" class="form-check-label">Dauerangebot</label>
</div>
</div>
</div>
<!-- ==================== BLOCK 3: TEXTBEDARF (Beschreibung, Allergene) ==================== -->
<div class="card mb-4 shadow-sm border-info">
<div class="card-body p-3">
<h6 class="card-title text-info mb-3">Beschreibung &amp; Nährstoffe</h6>
<!-- Allergene (TextArea) -->
<div class="mb-3">
{{ form.allergene }}
{% if form.allergene.errors %}<p class="text-danger small mt-1">{{ form.allergene.errors }}</p>{% endif %}
</div>
<!-- Beschreibung (TextArea) -->
<div class="mb-3">
{{ form.beschreibung }}
{% if form.beschreibung.errors %}<p class="text-danger small mt-1">{{ form.beschreibung.errors }}</p>{% endif %}
</div>
</div>
</div>
<!-- ==================== BLOCK 4: BILDER & Upload (Media) ==================== -->
<div class="card mb-5 shadow-sm border-secondary">
<div class="card-header bg-secondary text-white p-3">Mediendateien hochladen</div>
<div class="card-body">
<div class="mb-3">
{{ form.bilder }}
{% if form.bilder.errors %}<p class="text-danger small mt-1">{{ form.bilder.errors }}</p>{% endif %}
</div>
</div>
</div>
<!-- Submit Button Container -->
<div class="d-grid gap-2">
<button type="submit" class="btn btn-lg btn-success">Gericht Speichern &amp; Veröffentlichen</button>
</div>
</form> </form>
</div> </div>
</div>
</body> </body>
</html> </html>
+1
View File
@@ -2,6 +2,7 @@ from django.urls import path
from .views import GerichtListView from .views import GerichtListView
from .views import SpeiseplanView # Achte auf den neuen Klassennamen! from .views import SpeiseplanView # Achte auf den neuen Klassennamen!
from .views import BestellSummaryView from .views import BestellSummaryView
from .views import GerichtCreateView
urlpatterns = [ urlpatterns = [
path('speisekarte/', GerichtListView.as_view(), name='speisekarte'), path('speisekarte/', GerichtListView.as_view(), name='speisekarte'),
+27 -7
View File
@@ -111,18 +111,38 @@ class GerichtCreateView(CreateView):
objekt = self.object # Gibt uns das gerade gespeicherte Objekt zurück objekt = self.object # Gibt uns das gerade gespeicherte Objekt zurück
return reverse('speisekarte') return reverse('speisekarte')
# 🔑 DIES ist die entscheidende Methode!
def form_valid(self, form): def form_valid(self, form):
""" """
Übersteuert die Standard-Logik: Wird ausgeführt, wenn das Formular erfolgreich validiert wurde.
1. Speichert zuerst das Gericht (über super()). Wir speichern zuerst das Gericht (Parent) und dann alle Bilder (Children).
2. Leitet danach auf ein success/confirmation Pattern um.
""" """
super().form_valid() # Das eigentliche Speichern des Objekts # 1. Super-Call: Speichere das Hauptobjekt (das Gericht) ZUERST!
# Dadurch wird der 'gericht_pk' generiert, den wir für die Kinder brauchen.
gericht_instance = super().form_valid()
# Hier ist der Haken an Django: Die View wird beim Erfolg automatisch weitergeleitet. # Das gerade erstellte Gericht ist nun gespeichert und hat einen primären Key (PK).
# Wir verwenden einen kleinen Trick, damit wir den Namen im Kontext haben. newly_created_dish = self.object
return redirect('success_nach_gerichterstellung') # Leiten auf eine temporäre URL um # 2. Bildverarbeitung: Gehe alle Dateien im POST-Request durch
uploaded_files = self.request.FILES.getlist('bilder') # HIER das Feld 'bilder' verwenden!
if uploaded_files:
print(f"DEBUG: Es wurden {len(uploaded_files)} Bilder verarbeitet.")
for file in uploaded_files:
# 3. Für jedes Bild erstellen wir ein neues GerichtBild-Objekt (Child)
try:
GerichtBild.objects.create(
gericht=newly_created_dish, # Verknüpfung zum Parent
image=file, # Das hochgeladene File-Objekt
sort_order=0 # Standardmäßig in die Queue
)
except Exception as e:
print(f"WARNUNG beim Speichern des Bildes {file.name}: {e}")
# 4. Die View wird automatisch weitergeleitet, dank super().form_valid()
return redirect('speisekarte') # Name der URL nach Erfolg
# Optional: Setzen des initialen Datenzustands (z.B. Datum, falls relevant) # Optional: Setzen des initialen Datenzustands (z.B. Datum, falls relevant)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):