<span id="deploy-django-on-lizard" />

# Django auf Lizard bereitstellen

Führe Django auf Lizard mit Gunicorn und dem WSGI-Modul deines Projekts auf Port `8000` aus. Bereite Produktionseinstellungen, Datenbankzugriff und die Bereitstellung statischer Dateien vor, bevor du dich auf die bereitgestellte App verlässt. `manage.py runserver` ist ein Entwicklungsserver.

<span id="set-the-start-command" />

## Startbefehl festlegen

Diese Anleitung setzt `manage.py`, `requirements.txt` und ein Projektpaket namens `config` voraus, das `wsgi.py` enthält. Ersetze `config` durch deinen tatsächlichen Paketnamen.

Nimm Django, Gunicorn und den Datenbanktreiber, den deine App verwendet, in deine getesteten Anforderungen auf. Füge ein `Procfile` hinzu:

```text
web: gunicorn config.wsgi:application --bind 0.0.0.0:8000
```

Das explizite Modul vermeidet Unklarheiten, wenn Einstellungen in verschachtelten Paketen liegen. Eine ASGI-App mit WebSockets benötigt einen ASGI-Server und einen anderen Startbefehl; diese Anleitung behandelt WSGI.

| Einstellung | Wert |
|---|---|
| Installation | `pip install -r requirements.txt` |
| Laufzeit | Gunicorn mit deinem WSGI-Modul |
| Service-Port | `8000` |
| Arbeitsverzeichnis | Verzeichnis, das `manage.py` enthält |

<span id="prepare-production-settings" />

## Produktionseinstellungen vorbereiten

Django erwartet ein `DATABASES`-Wörterbuch in `settings.py`. Wenn du `DATABASE_URL` nur für den Service setzt, verbindet das Django nicht mit PostgreSQL. Die folgenden Schritte erstellen [Managed Postgres](https://lizard.build/de/docs/addons/postgres), übergeben seine URL an die App und wandeln diese URL in Djangos Verbindungseinstellungen um.

Füge diese Pakete zu `requirements.txt` hinzu und behalte alle weiteren Abhängigkeiten bei, die deine App benötigt. Dies sind die Versionen, die im [vollständigen Beispiel](https://github.com/lizard-build/docs/tree/main/_examples/django) verwendet werden:

```text
Django==6.1.1
gunicorn==26.2.0
whitenoise==6.12.0
psycopg[binary]==3.3.5
dj-database-url==3.1.2
```

`psycopg` ist der PostgreSQL-Treiber. `dj-database-url` liest Datenbankname, Benutzer, Passwort, Host und Port aus der Verbindungs-URL. Ersetze die entsprechenden Einstellungen in der `settings.py` deines Projekts durch diesen Block; behalte deine vorhandenen Apps, Middleware, Vorlagen und weiteren Einstellungen bei:

```python
import os
from pathlib import Path

import dj_database_url
from django.core.exceptions import ImproperlyConfigured

BASE_DIR = Path(__file__).resolve().parent.parent


def required_env(name):
    value = os.environ.get(name, "").strip()
    if not value:
        raise ImproperlyConfigured(f"Set the {name} environment variable.")
    return value


def env_list(name):
    values = [item.strip() for item in required_env(name).split(",") if item.strip()]
    if not values:
        raise ImproperlyConfigured(f"Set at least one value in {name}.")
    return values


SECRET_KEY = required_env("SECRET_KEY")
debug_value = os.environ.get("DEBUG", "false").strip().lower()
if debug_value not in {"true", "false"}:
    raise ImproperlyConfigured("DEBUG must be true or false.")
DEBUG = debug_value == "true"
ALLOWED_HOSTS = env_list("ALLOWED_HOSTS")
CSRF_TRUSTED_ORIGINS = env_list("CSRF_TRUSTED_ORIGINS")
DATABASES = {
    "default": dj_database_url.parse(
        required_env("DATABASE_URL"),
        conn_max_age=60,
        conn_health_checks=True,
    )
}
```

Lass keine ältere Zuweisung für `DATABASES`, `SECRET_KEY` oder `DEBUG` weiter unten in der Datei stehen: Sie würde diese Werte überschreiben. Dieses Beispiel erfordert nichtleere Werte für `SECRET_KEY`, `DATABASE_URL`, `ALLOWED_HOSTS` und `CSRF_TRUSTED_ORIGINS`. Fehlende oder leere Werte stoppen den Start mit dem Namen der Variablen. `DEBUG` ist standardmäßig `false`; setze es im bereitgestellten Service auf `false`.

`ALLOWED_HOSTS` akzeptiert kommagetrennte Hostnamen ohne Schema oder Pfad, zum Beispiel `app.example.com,www.example.com`. `CSRF_TRUSTED_ORIGINS` akzeptiert kommagetrennte Origins mit Schema, zum Beispiel `https://app.example.com`. Gib bei lokalen Origins einen Port an, wenn einer verwendet wird. Das Beispiel erfordert eine explizite Origin-Liste; Django selbst erlaubt eine leere Liste für Apps, die keine zusätzlichen vertrauenswürdigen Origins benötigen. Vertraue nur Origins, die Formulare an diese App senden sollen. Diese Einstellungen ersetzen keine CSRF-Tokens.

Die URL stammt aus einem [Secret oder einer Referenz](https://lizard.build/de/docs/variables) des Service, nicht aus einem im Quellcode gespeicherten Wert. Verwende kein containerlokales SQLite als dauerhafte Datenbank für eine bereitgestellte App. Siehe [Verwendung von dj-database-url](https://pypi.org/project/dj-database-url/) für das Parsen von URLs und Verbindungsoptionen.

Wenn deine lokalen Umgebungsvariablen gesetzt sind und PostgreSQL erreichbar ist, führe aus:

```bash
python -m pip install -r requirements.txt
python manage.py check --deploy
gunicorn config.wsgi:application --bind 0.0.0.0:8000
```

Prüfe Djangos [Deployment-Checkliste](https://docs.djangoproject.com/en/6.1/howto/deployment/checklist/) auf Einstellungen, die für deine App relevant sind. Das kleine Beispiel konfiguriert nicht jede Produktionseinstellung für Sicherheit. Prüfe Proxy- und HTTPS-Verhalten mit der tatsächlichen öffentlichen URL, bevor du sichere Cookies, HSTS oder Weiterleitungen aktivierst. Vertraue weitergeleiteten HTTPS-Headern nur, wenn der Proxy sie kontrolliert.

<span id="plan-static-files-and-migrations" />

## Statische Dateien und Migrationen planen

Gunicorn allein stellt Djangos statische Dateien nicht bereit. Wähle entweder für die App konfigurierte Static-File-Middleware oder einen separaten Host für statische Dateien. Sammle Assets in den Pfad, den das Setup bereitstellt. Der Python-Pfad von lizardpack auf Basis von Requirements führt `collectstatic` nicht automatisch aus; füge ihn in einen vollständigen Dockerfile-Build ein, wenn die App diesen Schritt benötigt.

Für WhiteNoise belasse `django.contrib.staticfiles` in `INSTALLED_APPS` und füge `whitenoise.middleware.WhiteNoiseMiddleware` direkt nach Djangos `SecurityMiddleware` ein. Füge diese Einstellungen hinzu oder aktualisiere sie:

```python
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
    "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
    "staticfiles": {
        "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
    },
}
```

Behalte dein vorhandenes Backend für `STORAGES["default"]` bei, wenn die App Uploads bereits woanders speichert. WhiteNoise stellt gesammelte statische Assets bereit, nicht von Benutzern hochgeladene Dateien.

Verwende dieses Dockerfile im Projektstammverzeichnis:

```dockerfile
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN SECRET_KEY=build-only-placeholder \
    DATABASE_URL=postgresql://build:build@127.0.0.1:1/build \
    ALLOWED_HOSTS=localhost CSRF_TRUSTED_ORIGINS=http://localhost \
    python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]
```

Die Werte in der Zeile `RUN` gelten nur für `collectstatic`. Die Datenbank-URL ist ein Platzhalter, der auf einen ungenutzten lokalen Port zeigt; dort läuft keine Datenbank. Beim Laden der Einstellungen wird die URL geparst, aber das Sammeln von Assets benötigt keine Datenbankverbindung. Führe keine Datenbankabfragen aus Modulimporten oder `AppConfig.ready()` aus. Das Beispiel übergibt außerdem `collectstatic` bei deaktiviertem Containernetzwerk.

Diese Zuweisungen werden nicht zu Umgebungsvariablen zur Laufzeit. Setze vor dem Start für den Service ein neues `SECRET_KEY` und die echte Datenbankreferenz. Übergib keine Produktionsgeheimnisse an den Docker-Build.

Erstelle `.dockerignore` und schließe dieselben Pfade von [Source Uploads](https://lizard.build/de/docs/cli/up) aus:

```text
.env*
.venv/
venv/
.git/
__pycache__/
*.pyc
*.sqlite3
staticfiles/
```

Bewahre Benutzer-Uploads in dauerhaftem Speicher auf, etwa [Managed Object Storage](https://lizard.build/de/docs/addons/storage), statt im statischen Verzeichnis des Containers. Prüfe [Speicherung und Wiederherstellung](https://lizard.build/de/docs/platform/storage-and-recovery).

Behandle Datenbankmigrationen als separaten Release-Schritt. Sichere Daten bei Bedarf, mache Migrationen sicher erneut ausführbar und wende sie an, bevor Requests das neue Schema benötigen. Ein Befehl vor dem Bereitstellen ist kein Ersatz dafür, Nebenläufigkeit und Fehlerverhalten zu prüfen.

<span id="deploy-and-verify" />

## Bereitstellen und prüfen

Nach dem [CLI-Setup](https://lizard.build/de/docs/framework-guides#prepare-the-project) verbindest du einen [GitHub-Service](https://lizard.build/de/docs/deploy/github), konfigurierst seine Secrets und Produktionseinstellungen und stellst ihn mit Container-Port `8000` bereit. Für ein neues uploadbasiertes Projekt:

```bash
lizard init --name django-app
lizard add --service web
```

Erstelle [Managed Postgres](https://lizard.build/de/docs/addons/postgres) in diesem Projekt. Wenn du bereits eine Instanz hast, überspringe `add postgres` und verwende ihren Namen in der Referenz:

```bash
lizard add postgres --name postgres
```

Setze die [Service Secrets](https://lizard.build/de/docs/cli/secrets) der App. Das `postgres` in `${{postgres.DATABASE_URL}}` bezeichnet den Datenbank-Service, nicht einen Django-Datenbankalias. Lizard löst die [Referenz](https://lizard.build/de/docs/variables/references) beim Bereitstellen auf und injiziert die resultierende URL in den Prozess `web`. Die einfachen Anführungszeichen verhindern, dass deine Shell die Referenz expandiert. Der oben stehende Block `settings.py` wandelt die URL dann in `DATABASES["default"]` um.

```bash
DJANGO_SECRET_KEY="$(python -c 'import secrets; print(secrets.token_urlsafe(48))')"
lizard secrets set SECRET_KEY="$DJANGO_SECRET_KEY" \
  DATABASE_URL='${{postgres.DATABASE_URL}}' DEBUG=false \
  ALLOWED_HOSTS=localhost CSRF_TRUSTED_ORIGINS=http://localhost \
  --service web
lizard up --service web --port 8000
lizard ps --json
```

`localhost` ist eine vorläufige Host-Einstellung, die es dem Prozess erlaubt zu starten, während du seinen öffentlichen Hostnamen ermittelst; öffentliche Requests geben 400 zurück. Wenn du den Hostnamen bereits kennst, setze ihn vor dem ersten Upload. Andernfalls ersetze diese Platzhalter durch den Hostnamen und die HTTPS-Origin, die für deinen Service zurückgegeben wurden:

```bash
lizard secrets set ALLOWED_HOSTS=YOUR_PUBLIC_HOST \
  CSRF_TRUSTED_ORIGINS=https://YOUR_PUBLIC_HOST --service web
```

Ein fehlendes Referenzziel oder ein fehlender Schlüssel kann zu einer leeren Zeichenfolge aufgelöst werden. Die Prüfung auf erforderliche Werte stoppt Django dann mit `Set the DATABASE_URL environment variable.`. Prüfe den Namen des Datenbank-Service und seinen Schlüssel `DATABASE_URL`; gib die Verbindungs-URL nicht in Logs aus.

Die Variablenänderung startet den Prozess neu. Schließe lokale virtuelle Umgebungen, Secrets und lokale Datenbanken von Uploads aus. Sobald der Prozess läuft, wende Migrationen an:

```bash
lizard ssh --service web -- python manage.py migrate --noinput
```

Führe den Befehl erneut aus, um zu prüfen, dass keine Migrationen mehr ausstehen. Betrachte eine erfolgreiche Port-Prüfung nicht als Nachweis dafür, dass Tabellen existieren.

Prüfe die App-URL, eine datenbankgestützte Seite, eine Formularübermittlung und ein statisches Asset. Wenn die App Django admin verwendet, prüfe auch dessen CSS. Lies `lizard logs --service web --json` auf Import- oder Einstellungsfehler. Eine 400-Antwort bedeutet oft, dass `ALLOWED_HOSTS` falsch ist; fehlendes CSS bedeutet meist, dass statische Assets nicht gesammelt oder nicht bereitgestellt wurden. Prüfe den tatsächlichen Fehler, bevor du Einstellungen änderst.

Siehe [getestete Versionen und Cloud-Ergebnisse](https://lizard.build/de/docs/framework-guides/validation) für die Deployment-Prüfungen vom 9. September 2026 und ihre Grenzen.


<span id="reproduce-this-example" />

## Dieses Beispiel reproduzieren

Das [Django-Beispiel](https://github.com/lizard-build/docs/tree/main/_examples/django) enthält die Einstellungen, das Dockerfile, Requirements, ein datenbankgestütztes Formular und einen Test-Runner. Kopiere es in ein eigenständiges Verzeichnis und führe `python3 tests/verify.py` aus, während Docker läuft. Es baut das Image, startet lokales PostgreSQL, prüft Migrationen und HTTP-Verhalten und stoppt seine Test-Container. Es erstellt keine Lizard-Services. In der README findest du die Prüfungen und beibehaltenen Docker-Ressourcen.

Die Prüfung vom 14. September 2026 deckt dieses überarbeitete Beispiel in lokalen Linux-Containern ab. Die oben verlinkten früheren Cloud-Ergebnisse beziehen sich auf die Anleitung vom 9. September; die überarbeiteten Einstellungen hatten noch kein neues Cloud-Deployment.
