Compare commits

...

8 Commits

45 changed files with 578 additions and 13 deletions
+3
View File
@@ -0,0 +1,3 @@
__pycache__/
*.pyc
+5
View File
@@ -0,0 +1,5 @@
[Buildset]
BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x10\x00S\x00G\x00T\x00M\x00e\x00n\x00s\x00a)
[Project]
VersionControlSupport=kdevgit
+26 -4
View File
@@ -6,12 +6,15 @@ Muster-Implementation durch die Lehrkraft in python3 mit Django
## To get started
## Vorbereitung der virtuellen Entwicklungsumgebung
in einem Projekt-Vezeichnis über dem eigentlichen SGTMensa-Projekt-Ordner
```bash
cd SGTMensa
python3 -m venv .venv
source .venv/bin/activate
pip install django
django-admin startproject mensa-core .
pip install pillow
```
Falls eine fish-Shell vorliegt:
@@ -19,9 +22,27 @@ Falls eine fish-Shell vorliegt:
python3 -m venv .venv
source .venv/bin/activate.fish
pip install django
django-admin startproject mensa-core .
pip install pillow
```
## Klonen des eigentlichen Projekts
```bash
git clone https://git.sgtlernen.de/mputzlocher/SGTMensa.git
```
## Starten des Servers
```bash
cd SGTMensa
python manage.py runserver
```
---
# Erweiterte Erklärung einzelner Schritte
### Schritt 1: Die Arbeitsumgebung isolieren (Virtual Environment)
Bevor du auch nur eine Zeile Code schreibst, erstelle eine eigene Umgebung. Das verhindert, dass sich verschiedene Projekte auf deinem Rechner gegenseitig stören.
@@ -42,9 +63,10 @@ Installiere nur das Nötigste. Für den Start reicht Django.
```bash
pip install django
pip install pillow
```
### Schritt 3: Das Projekt initialisieren
### Schritt 3: Das Projekt initialisieren (nur einmalig, beim ersten Start des Projekts)
Erstelle die Struktur eines Django-Projekts.
BIN
View File
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+63 -2
View File
@@ -3,7 +3,50 @@ from django.contrib import admin
# Register your models here.
from .models import Person # Ersetze dies durch deine echten Klassennamen
admin.site.register(Person)
# DELTE: Die Option, Kinder zuzuordnen soll zunächst nicht umgesetzt werden.
# class PersonInlineChildren(admin.TabularInline):
# """
# Inline für das Feld ``children`` (Schüler eines Elternteils).
# Nur bei Eltern (Rolle Mitarbeit/Chef) sichtbar.
# """
# model = Person.children.through # Das durchschnittliche JoinModel
# verbose_name = 'Kind'
# verbose_name_plural = 'Kinder'
# extra = 0 # Keine leeren Zeilen anzeigen
#
# # ----------------------------------------------------------
# # **WICHTIG:** Hier wird der zu verwendende FK explizit genannt
# # ----------------------------------------------------------
# fk_name = 'person' # <-- legt fest, welcher FK gemeint ist
#
# def has_add_permission(self, request, obj=None):
# # Erlaube Hinzufügen nur für Eltern (Rolle Eltern)
# return obj and obj.rolle in ('eltern')
#
# def formfield_for_foreignkey(self, db_field, request, **kwargs):
# """
# Verhindere, dass ein Benutzer sich selbst als Kind hinzufügt.
# """
# if db_field.name == "person":
# # Hier handelt es sich um den FK auf die Person (das Ziel)
# kwargs["queryset"] = Person.objects.exclude(pk=self.instance.pk)
# return super().formfield_for_foreignkey(db_field, request, **kwargs)
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
list_display = ('user', 'rolle', 'klasse')
search_fields = ['user__username', 'klasse']
ordering = ('rolle',)
# # ---- Inline für Eltern (Rolle Mitarbeit/Chef) ----
# inlines = [PersonInlineChildren] # Zeigt das KinderInline nur bei passenden Rollen an
def has_change_permission(self, request, obj=None):
"""
Optional: Verhindere, dass ein Schüler seine eigene Rolle oder die Zuordnung ändert.
"""
return super().has_change_permission(request, obj)
from .models import Schulwoche # Ersetze dies durch deine echten Klassennamen
@@ -19,7 +62,7 @@ admin.site.register(Kategorie)
from .models import Gericht # Ersetze dies durch deine echten Klassennamen
admin.site.register(Gericht)
# admin.site.register(Gericht)
from .models import Menue # Ersetze dies durch deine echten Klassennamen
@@ -29,3 +72,21 @@ from .models import Bestellung # Ersetze dies durch deine echten Klassennamen
admin.site.register(Bestellung)
from .models import Bewertung
admin.site.register(Bewertung)
from .models import GerichtBild
admin.site.register(GerichtBild)
class GerichtBildInline(admin.TabularInline):
model = GerichtBild
extra = 0 # Keine leeren Zeilen anzeigen (kann später auf 1 erhöht werden)
readonly_fields = ('image',) # Optional: Nur zur Anzeige, nicht editierbar
@admin.register(Gericht)
class GerichtAdmin(admin.ModelAdmin):
list_display = ('name', 'kategorie')
inlines = [GerichtBildInline] # Das Inline erscheint direkt unter jedem Gericht
@@ -0,0 +1,72 @@
# Generated by Django 5.2.14 on 2026-05-15 09:28
import datetime
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mensa_app', '0004_alter_gericht_time_creation_and_more'),
]
operations = [
migrations.AlterField(
model_name='bestellung',
name='id',
field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='gericht',
name='id',
field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='gericht',
name='time_creation',
field=models.TimeField(default=datetime.datetime(2026, 5, 15, 9, 28, 5, 292454)),
),
migrations.AlterField(
model_name='gericht',
name='time_last_change',
field=models.TimeField(default=datetime.datetime(2026, 5, 15, 9, 28, 5, 292454)),
),
migrations.AlterField(
model_name='kategorie',
name='id',
field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='menue',
name='id',
field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='person',
name='id',
field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='speiseplantag',
name='id',
field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.CreateModel(
name='Bewertung',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sterne', models.IntegerField(choices=[(1, '★☆☆☆☆'), (2, '★★☆☆☆'), (3, '★★★☆☆'), (4, '★★★★☆'), (5, '★★★★★')], default=3)),
('kommentar', models.TextField(blank=True, null=True)),
('datum', models.DateTimeField(auto_now_add=True)),
('ist_verifiziert', models.BooleanField(default=False, help_text='Wird automatisch auf True gesetzt, wenn eine bezahlte Bestellung vorliegt.')),
('gericht', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bewertungen', to='mensa_app.gericht')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bewertungen', to='mensa_app.person')),
],
options={
'verbose_name': 'Bewertung',
'verbose_name_plural': 'Bewertungen',
'unique_together': {('user', 'gericht')},
},
),
]
@@ -0,0 +1,52 @@
# Generated by Django 6.0.5 on 2026-05-19 20:13
import datetime
import django.core.validators
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mensa_app', '0004_alter_gericht_time_creation_and_more'),
]
operations = [
migrations.AlterField(
model_name='gericht',
name='time_creation',
field=models.TimeField(default=datetime.datetime(2026, 5, 19, 20, 13, 52, 449584)),
),
migrations.AlterField(
model_name='gericht',
name='time_last_change',
field=models.TimeField(default=datetime.datetime(2026, 5, 19, 20, 13, 52, 449584)),
),
migrations.CreateModel(
name='GerichtBild',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(help_text='Bitte nur JPEG-Dateien hochladen.', upload_to='gerichte_bilder/', validators=[django.core.validators.FileExtensionValidator(['jpg', 'jpeg'])])),
('sort_order', models.PositiveIntegerField(default=0)),
('gericht', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bilder', to='mensa_app.gericht')),
],
),
migrations.CreateModel(
name='Bewertung',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sterne', models.IntegerField(choices=[(1, '★☆☆☆☆'), (2, '★★☆☆☆'), (3, '★★★☆☆'), (4, '★★★★☆'), (5, '★★★★★')], default=3)),
('kommentar', models.TextField(blank=True, null=True)),
('datum', models.DateTimeField(auto_now_add=True)),
('ist_verifiziert', models.BooleanField(default=False, help_text='Wird automatisch auf True gesetzt, wenn eine bezahlte Bestellung vorliegt.')),
('gericht', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bewertungen', to='mensa_app.gericht')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bewertungen', to='mensa_app.person')),
],
options={
'verbose_name': 'Bewertung',
'verbose_name_plural': 'Bewertungen',
'unique_together': {('user', 'gericht')},
},
),
]
@@ -0,0 +1,24 @@
# Generated by Django 6.0.5 on 2026-05-19 20:24
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mensa_app', '0005_alter_gericht_time_creation_and_more'),
]
operations = [
migrations.AlterField(
model_name='gericht',
name='time_creation',
field=models.TimeField(default=datetime.datetime(2026, 5, 19, 20, 24, 22, 989880)),
),
migrations.AlterField(
model_name='gericht',
name='time_last_change',
field=models.TimeField(default=datetime.datetime(2026, 5, 19, 20, 24, 22, 989880)),
),
]
@@ -0,0 +1,32 @@
# Generated by Django 6.0.5 on 2026-05-19 20:27
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mensa_app', '0006_alter_gericht_time_creation_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='gerichtbild',
options={'verbose_name': 'Bild', 'verbose_name_plural': 'Bilder'},
),
migrations.AlterField(
model_name='gericht',
name='time_creation',
field=models.TimeField(default=datetime.datetime(2026, 5, 19, 20, 27, 3, 59858)),
),
migrations.AlterField(
model_name='gericht',
name='time_last_change',
field=models.TimeField(default=datetime.datetime(2026, 5, 19, 20, 27, 3, 59858)),
),
migrations.AlterUniqueTogether(
name='gerichtbild',
unique_together={('image', 'gericht')},
),
]
@@ -0,0 +1,30 @@
# Generated by Django 6.0.5 on 2026-05-19 20:43
import datetime
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mensa_app', '0007_alter_gerichtbild_options_and_more'),
]
operations = [
migrations.AlterField(
model_name='gericht',
name='time_creation',
field=models.TimeField(default=datetime.datetime(2026, 5, 19, 20, 43, 37, 765352)),
),
migrations.AlterField(
model_name='gericht',
name='time_last_change',
field=models.TimeField(default=datetime.datetime(2026, 5, 19, 20, 43, 37, 765352)),
),
migrations.AlterField(
model_name='gerichtbild',
name='image',
field=models.ImageField(help_text='Bitte nur JPEG-Dateien (.jpg/.jpeg) hochladen.', upload_to='gerichte_bilder/', validators=[django.core.validators.FileExtensionValidator(['jpg', 'jpeg'])]),
),
]
@@ -0,0 +1,24 @@
# Generated by Django 6.0.5 on 2026-05-19 21:49
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mensa_app', '0008_alter_gericht_time_creation_and_more'),
]
operations = [
migrations.AlterField(
model_name='gericht',
name='time_creation',
field=models.TimeField(default=datetime.datetime(2026, 5, 19, 21, 49, 30, 971985)),
),
migrations.AlterField(
model_name='gericht',
name='time_last_change',
field=models.TimeField(default=datetime.datetime(2026, 5, 19, 21, 49, 30, 971985)),
),
]
@@ -0,0 +1,14 @@
# Generated by Django 5.2.14 on 2026-06-12 09:13
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('mensa_app', '0005_alter_bestellung_id_alter_gericht_id_and_more'),
('mensa_app', '0009_alter_gericht_time_creation_and_more'),
]
operations = [
]
+226 -5
View File
@@ -3,17 +3,74 @@ from django.contrib.auth.models import User
from django.utils import timezone
import datetime
import os
from django.db import models
from django.core.exceptions import ValidationError
from django.core.validators import FileExtensionValidator
# Pillow ist für die Bildbearbeitung zuständig (installiere per pip install pillow)
from PIL import Image as PilImage
# Optional: Wenn du später Thumbnails mit sorl-thumbnail nutzen möchtest:
# from sorl.thumbnail import get_thumbnail
class Person(models.Model):
"""Repräsentiert Schüler oder Lehrer."""
user = models.OneToOneField(User, on_delete=models.CASCADE)
rolle = models.CharField(max_length=20, choices=[('schueler', 'Schüler'), ('lehrer', 'Lehrer'), ('mitarbeiter','Mensa-Mitarbeiter'), ('chef','Mensa-Leitung')])
rolle = models.CharField(max_length=20, choices=[('schueler', 'Schüler'), ('eltern', 'Eltern'), ('lehrer', 'Lehrer'), ('mitarbeiter','Mensa-Mitarbeiter'), ('chef','Mensa-Leitung')])
klasse = models.CharField(max_length=4, blank=True, null=True) # Nur für Schüler relevant
class Meta:
verbose_name_plural = "Personen"
def __str__(self):
return f"{self.user.username} ({self.rolle})"
# DELETE: Die Eltern-Kinder Beziehung soll zunächst nicht umgesetzt werden.
# # ----------------------------------------------------------------------
# # Neue ManytoMany Beziehung: „Elternteil ↔ Schüler“
# # ----------------------------------------------------------------------
# children = models.ManyToManyField(
# 'self',
# blank=True,
# related_name='parents', # → für einen Schüler: person.parents.all()
# symmetrical=False, # Verhindert eine zirkuläre RückBeziehung (nicht benötigt)
# help_text=(
# "Eltern können hier die ihnen zugewiesenen Schüler hinzufügen. Für Schüler bleibt dieses Feld leer."
# )
# )
#
# # ------------------------------------------------------------------
# # Optional: Hilfsmethode, die alle Kinder zurückgibt, ggf. mit einer Sortierung
# def get_children_sorted(self):
# # Das `order_by('name')` ist ein Beispiel du kannst deine eigene Sortierung verwenden.
# return self.children.order_by('name')
#
# class Meta:
# verbose_name_plural = "Personen"
#
# def __str__(self):
# if self.children.exists():
# # Wir sammeln alle KinderNamen in einem String, z.B. mit Komma getrennt.
# children_names = ', '.join([c.name for c in self.children.all()])
# return f"{self.user.username} ({children_names}) ({self.rolle})"
# else:
# return f"{self.user.username} ({self.rolle})"
#
# def clean(self):
# """
# Überprüfung für die ManyToMany-Beziehung ``children``:
# - Nur Personen mit der Rolle 'mitarbeiter' oder 'chef' (Eltern) dürfen Kinder haben.
# - Ein Schüler darf sich selbst nicht als Kind hinzufügen.
# """
# super().clean() # ruft die Validierungen der Elternklasse auf (hier User/Model)
#
# if self.rolle in ('mitarbeiter', 'chef', 'lehrer'):
# # NichtEltern dürfen keine Kinder besitzen
# if self.children.exists():
# raise ValidationError(
# "Nur Benutzer mit der Rolle 'Eltern' "
# "dürfen Schüler zuordnen (children)."
# )
# else:
# # Eltern: Verhindere, dass sie sich selbst als Kind eintragen
# if self in self.children.all():
# raise ValidationError("Ein Benutzer darf nicht gleichzeitig Elternteil und eigener Kind sein.")
class Schulwoche(models.Model):
@@ -133,3 +190,167 @@ class Bestellung(models.Model):
verbose_name = "Bestellung"
verbose_name_plural = "Bestellungen"
from django.core.exceptions import ValidationError
class Bewertung(models.Model):
"""
Repräsentiert die Bewertung eines Gerichts durch einen Nutzer.
"""
class Sterne(models.IntegerChoices):
EINER = 1, '★☆☆☆☆'
ZWEI = 2, '★★☆☆☆'
DREI = 3, '★★★☆☆'
VIER = 4, '★★★★☆'
FUENF = 5, '★★★★★'
user = models.ForeignKey('Person', on_delete=models.CASCADE, related_name='bewertungen')
gericht = models.ForeignKey('Gericht', on_delete=models.CASCADE, related_name='bewertungen')
sterne = models.IntegerField(choices=Sterne.choices, default=3)
kommentar = models.TextField(blank=True, null=True)
datum = models.DateTimeField(auto_now_add=True)
# Das Feld für die Verifizierung
ist_verifiziert = models.BooleanField(
default=False,
help_text="Wird automatisch auf True gesetzt, wenn eine bezahlte Bestellung vorliegt."
)
class Meta:
# Verhindert, dass ein Nutzer dasselbe Gericht mehrfach bewertet
unique_together = ('user', 'gericht')
verbose_name = "Bewertung"
verbose_name_plural = "Bewertungen"
def __str__(self):
return f"{self.user.user.username} bewertet {self.gericht.name} mit {self.sterne} Sternen"
def clean(self):
"""
Hier können wir zusätzliche Validierungen einbauen.
"""
if self.sterne < 1 or self.sterne > 5:
raise ValidationError("Die Bewertung muss zwischen 1 und 5 Sternen liegen.")
def check_verifizierung(self):
"""
Ein hilfreicher Service-Method, um den Status der Verifizierung
automatisch zu prüfen.
"""
# Wir prüfen, ob es eine Bestellung für diesen User und dieses Gericht gibt,
# die bereits als 'bezahlt' markiert ist.
exists = Bestellung.objects.filter(
person=self.user,
menue__gericht=self.gericht,
bezahlt=True
).exists()
if exists:
self.ist_verifiziert = True
return self.ist_verifiziert
class GerichtBild(models.Model):
"""
Speichert ein hochgeladenes Bild für ein Gericht.
- Kann später beliebig erweitert werden (z.B. Beschreibung, Sortierreihenfolge).
"""
gericht = models.ForeignKey(
'Gericht', # Verweis auf das Gericht
on_delete=models.CASCADE, # Wenn das Gericht gelöscht wird, lösche auch die Bilder
related_name='bilder' # -> Gericht.bilder.all()
)
image = models.ImageField( # Typ: BildDatei (JPEG)
upload_to='gerichte_bilder/', # Unterordner im MEDIA_ROOT
validators=[FileExtensionValidator(['jpg', 'jpeg'])], # Nur JPEG zulassen
help_text="Bitte nur JPEG-Dateien (.jpg/.jpeg) hochladen."
)
# Optional: Sortierreihenfolge, wenn mehrere Bilder angezeigt werden sollen
sort_order = models.PositiveIntegerField(default=0)
class Meta:
# Verhindert, dass ein Bild mehrfach zu einem Gericht angelegt wird
unique_together = ('image', 'gericht')
verbose_name = "Bild"
verbose_name_plural = "Bilder"
def clean(self):
"""
Zusätzliche Validierung wird vor dem Speichern aufgerufen,
wenn du .full_clean() oder ModelForm nutzt.
"""
# Prüfe ob die Datei tatsächlich ein JPEG ist (optional, da Validator schon hilft)
_, ext = os.path.splitext(self.image.name.lower())
if ext not in ('.jpg', '.jpeg'):
raise ValidationError('Nur JPEG-Dateien sind erlaubt.')
def _resize_image_if_needed(self):
"""
Verkleinert das hochgeladene Bild auf maximal 640 × 480px
(beibehält Seitenverhältnis). Überschreibt die Originaldatei.
"""
# Sicherstellen, dass ein Dateiname vorhanden ist und die Datei lesbar ist
if not self.image or not self.image.name:
return
try:
# Öffne das Bild mit Pillow .open('rb') liefert eine FileInstanz
with self.image.open('rb') as img_file:
pil_img = PilImage.open(img_file)
pil_img = pil_img.convert('RGB')
# thumbnail passt das Bild in die Grenzen (640×480) und behält
# das Seitenverhältnis bei.
pil_img.thumbnail((640, 480), PilImage.LANCZOS)
# Schreibe das verkleinerte Bild zurück in dieselbe Datei.
# storage = self.image.field.storage # z.B. FileSystemStorage
storage = self.image.field.storage
path = self.image.path
with storage.open(path, 'wb') as out_f:
pil_img.save(out_f, format='JPEG', quality=85)
except Exception as exc:
# Im ProduktivBetrieb wäre hier ein Logging sinnvoll.
print(f'Fehler beim Verkleinern von {self.image.name}: {exc}')
def save(self, *args, **kwargs):
"""
Überschreibe das Speichern, um das Bild automatisch auf 640×480 px
(oder darunter) zu verkleinern. Das Ergebnis überschreibt die OriginalDatei.
"""
# 1️⃣ Führe die normale save() aus nötig, damit wir später den Pfad haben
super().save(*args, **kwargs)
# 2️⃣ Führe die Verkleinerung nur dann aus, wenn tatsächlich ein Bild da ist.
if self.image and self.image.name:
self._resize_image_if_needed()
def delete(self, *args, **kwargs):
"""
Erweiterte DeleteLogik:
1. Versuche die Datei aus dem Storage zu löschen (falls sie existiert).
2. Führe eigene Aktionen durch (z.B. Logging).
3. Rufe die StandardDeleteMethode auf, um den DBEintrag zu entfernen.
"""
# --- 1️⃣ Versuche die Datei zu löschen -------------------------------------------------
storage = self.image.field.storage # z.B. FileSystemStorage
path = self.image.path # vollständiger Pfad im Media-Ordner
try:
if storage.exists(path):
# Optional: Backup, Logging usw.
print(f"[DELETE] Bild entfernt: {path}")
storage.delete(path) # tatsächliches Löschen der Datei
except Exception as exc:
# Im ProduktivBetrieb besser mit logging.exception() umgehen
print(f"Warnung konnte die Bilddatei nicht löschen ({exc})")
# --- 2️⃣ Rufe die normale Django-DeleteLogik auf ------------------------------------
super().delete(*args, **kwargs) # löscht den DBEintrag
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -1
View File
@@ -10,11 +10,13 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/6.0/ref/settings/
"""
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # Ordner, in dem die OriginalDateien landen
MEDIA_URL = '/media/' # URL-Pfad für den Zugriff (bspw. http://localhost:8000/media/...)
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
+4 -1
View File
@@ -16,8 +16,11 @@ Including another URLconf
"""
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('mensa_app.urls')), # Schaltet die App-URLs frei
]
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)