Files
Inventory/docs/postgresql-migration.md
2026-09-29 08:43:05 +02:00

114 lines
5.2 KiB
Markdown

# SQLite → PostgreSQL mit Docker
Die Anwendung verwendet standardmäßig PostgreSQL. Es gibt keinen automatischen
Rückfall auf SQLite. Die folgenden Befehle sind vorbereitet; sie wurden auf dem
aktuellen Windows-Rechner ohne Docker nicht ausgeführt.
## Vorbereiten
Docker mit Compose muss verfügbar sein. Die Befehle im Projektverzeichnis ausführen.
Die bisherige Anwendung während Export, Import und Prüfung stoppen, damit keine
Änderungen nach dem Export verloren gehen. `db.sqlite3` und das Verzeichnis `media`
behalten und zusätzlich extern sichern. Niemals `docker compose down -v` ausführen,
wenn Volumes noch benötigte Daten enthalten.
`.env.example` nach `.env` kopieren und mindestens `SECRET_KEY`,
`POSTGRES_PASSWORD`, Domain, erlaubte Hosts und CSRF-Origin ersetzen.
`POSTGRES_HOST=db` beibehalten. Keine Zugangsdaten in Git ablegen.
```sh
docker compose config --quiet
docker compose --profile tools build web migration
docker compose stop web traefik
docker compose up -d --wait db
```
Die Zieldatenbank muss leer sein. Vor dem Import den Webdienst noch **nicht**
starten: Sein Startskript legt sonst bereits Seed-Daten an. Das Importkommando
verweigert einen Import, sobald Benutzer, Gruppen oder Inventardaten vorhanden sind.
Die zwei von Migration 0003 angelegten Standard-Gerätetypen werden als leere
Ausgangsbasis erkannt und innerhalb der Importtransaktion durch die Exportdaten
ersetzt. Ein vorhandenes PostgreSQL-System niemals zum Zweck des Imports leeren; stattdessen
eine neue Datenbank bzw. ein separates Compose-Projekt verwenden.
## Exportieren, Schema anlegen, importieren
```sh
docker compose run --rm migration scripts/export_sqlite.py /legacy/db.sqlite3 /transfer/sqlite-export.json
docker compose run --rm migration manage.py migrate --noinput
docker compose run --rm migration manage.py import_sqlite_export /transfer/sqlite-export.json
```
Der Export liest SQLite über eine schreibgeschützte Verbindung, erstellt einen
konsistenten temporären Snapshot und aktualisiert nur dessen Schema. Die originale
SQLite-Datei bleibt unverändert. Existiert die Exportdatei bereits, wird sie nicht
überschrieben; für einen weiteren Export einen neuen Dateinamen verwenden.
Der Export enthält Benutzer und Passwort-Hashes, Rollen, API-Schlüssel-Hashes,
Geräte, Archivstatus, Zusatzfelder, Einstellungen, Nummernfolge und Historie.
Content Types und Standardberechtigungen werden durch Migrationen angelegt und
über natürliche Schlüssel zugeordnet. Sitzungen werden nicht übernommen; Benutzer
müssen sich erneut anmelden. Der Import läuft transaktional und setzt die
PostgreSQL-ID-Sequenzen durch Djangos `loaddata` zurück. Die Gerätenummernfolge
wird mindestens auf die höchste vorhandene Gerätenummer gesetzt.
`backups/sqlite-export.json` enthält vertrauliche Daten und ist kein öffentliches
Download-Artefakt. Der Export setzt auf Linux Dateirechte `0600`. Der offline
gestartete Migrationscontainer läuft für den Zugriff auf das Backup-Verzeichnis
als root; der Webcontainer läuft weiterhin als unprivilegierter `app`-Benutzer.
## Uploads übernehmen
Der Datenbankexport enthält Dateiverweise, nicht die Dateien selbst. Bei einem
**neuen, leeren** `media_data`-Volume die bisherigen Uploads kopieren:
```sh
docker compose run --rm --entrypoint sh migration -c 'cp -a /legacy/media/. /app/media/ && chown -R app:app /app/media'
```
Ein bereits verwendetes Medienvolume zuerst sichern und seine Inhalte separat
abgleichen; der Kopierbefehl darf nicht ungeprüft über vorhandene Dateien laufen.
## Prüfen und starten
```sh
docker compose run --rm migration manage.py check
docker compose run --rm migration manage.py makemigrations --check --dry-run
docker compose run --rm -e DEBUG=true -e SECURE_SSL_REDIRECT=false migration manage.py test --noinput
docker compose up -d --build web traefik
docker compose ps
```
Die Tests verwenden eine separate PostgreSQL-Testdatenbank. Danach Anmeldung,
Geräteanzahl einschließlich Archiv, höchste Gerätenummer, Rollen, Historie und
Upload-Verweise mit dem bisherigen System vergleichen. Ein PostgreSQL-Backup
anlegen und testweise in eine separate leere Datenbank wiederherstellen:
```sh
sh scripts/backup.sh
```
Erst nach erfolgreicher Prüfung das alte System endgültig stilllegen. Für einen
Rückwechsel den neuen Webdienst stoppen und die alte Anwendung mit ihrer
unveränderten SQLite-Datei starten. Neue PostgreSQL-Änderungen werden nicht
automatisch zurück nach SQLite übertragen.
## Lokaler Docker-Betrieb ohne HTTPS-Domain
Nach dem Import alternativ nur Datenbank und Webdienst starten:
```sh
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build db web
```
Die Anwendung ist dann unter `http://localhost:8000` erreichbar. Der Port ist nur
an localhost gebunden. Diese Entwicklungsüberschreibung nicht auf dem VPS nutzen.
## Bearbeitungsversionen
Geräte besitzen nun eine ganzzahlige `revision`. Browser-Formulare senden
`expected_revision`; API-PATCH-Anfragen können ebenfalls dieses Feld verwenden.
Bestehende API-Clients mit `expected_updated_at` bleiben kompatibel. Veraltete
Versionen werden abgewiesen. Zeitstempel steigen auch bei geringer Auflösung der
Systemuhr strikt an. Migration `0006` setzt bestehende Geräte auf Revision 1.