Statische Dateien mit Django verwalten

  • 12. Januar 2022

Statische Dateien wie CSS Stylesheets, JavaScript Code, Logos und Bilder sind ein wichtiger Bestandteil jeder Webseite. Django-Projekte sind da keine Ausnahme. Das Web-Framework bietet dem Entwickler flexible Möglichkeiten, statische Dateien in kleinen und grossen Projekten zu verwalten. In dieser Anleitung zeige ich auf, wie die Grundkonfiguration aussieht, was jeweils dahintersteckt und was beachtet werden muss.

Unter statischen Dateien, engl. «static files», verstehen wir Dateien, die in einer Webseite enthalten sind und sich während des Seitenaufrufs nicht verändern. Dazu gehören Logos im .png oder .svg Format, Hintergrundbilder, JavaScript und CSS Stylesheets. Solche Dateien werden von den Webbrowsern lediglich einmal heruntergeladen, im Cache gespeichert und dann von dort immer wieder blitzschnell abgerufen. Ein Logo beispielsweise ist oft auf vielen Seiten eines Webauftritts vorhanden. Anstatt die Datei beim Surfen auf einer Webseite (z.B. von der Homepage zum Impressum), immer wieder neu über das Netzwerk zu laden, wird sie direkt aus dem Cache abgerufen. Das ist nicht nur um ein Vielfaches schneller, sondern minimiert ausserdem den Netzwerkverkehr.

Es wäre möglich, Dateien in den Django-Templates mit einem relativen Pfad direkt anzugeben. Zum Beispiel:

<img src="/static/images/mein-logo.jpg" alt="Unser Firmenlogo">

Dies empfiehlt sich jedoch nicht, da wir somit alle Flexibilität, die uns das Web-Framework mit statischen Dateien bietet, verlieren.

Wie das in Django gehandhabt wird, beschreibe ich hier.

Grundkonfiguration

Django bietet die Möglichkeit, statische Dateien vom Web-Framework verwalten zu lassen, von Haus aus. Dafür wird mit der Grundinstallation (wie hier erklärt) die App django.contrib.staticfiles für den Umgang mit statischen Dateien mitinstalliert. Gleichzeitig werden zwei Einstellungen in settings.py vorgenommen. Das sind folgende Einträge.

Unter INSTALLED_APPS den Eintrag 'django.contrib.staticfiles':

INSTALLED_APPS = [
    ...
    'django.contrib.staticfiles',
]

Und weiter unten folgende globale Variable:

STATIC_URL = '/static/'

Die in Django mitgelieferte App für die Verwaltung von statischen Dateien heisst django.contrib.staticfiles. Sie wird mit der Grundinstallation mitinstalliert.

Statische Dateien in Apps verwalten

Mit der globalen Variable STATIC_URL wird angegeben, dass Django beim Aufruf einer Datei in jeder App des Projektes nach einem Verzeichnis mit dem Namen static sucht.

Dazu muss in jeder App, die statische Dateien hat, ein Verzeichnis Namens static erstellt werden.

Wenn das Projekt zum Beispiel eine App namens events hat, legt man im Hauptverzeichnis dieser App ein neues Verzeichnis namens static an:

# ../mein_projekt/events/
mkdir static

Damit Django alle Dateien und Templates richtig zuordnet und nicht die erstbeste Datei mit dem gleichen Namen auswählt, muss im oben angelegten Verzeichnis nochmals ein neues Verzeichnis mit dem Namen der App, also events, angelegt werden:

# ../mein_projekt/events/static
mkdir events

Das sieht auf dem Computer, resp. Server so aus:

├── events
│   ├── static
│   │   └── events

Damit schafft man einen Namensraum («namespace») namens events für die statischen Dateien dieser App. Wird eine Datei dieser App aufgerufen, schaut Django zuerst in diesem Verzeichnis nach.

In diesem Verzeichnis legt man alle Dateien für die App Events ab. Hier empfiehlt es sich, nochmals eine Unterteilung vorzunehmen, und zwar nach Art der Dateien. Das Django-Projekt selber unterteilt seine contrib-Apps in folgende vier Verzeichnisse: img (für Bilder), js (für JavaScript Dateien), css (für CSS-Stylesheets) und fonts (für Schriften). Das sieht so aus:

├── events
│   ├── static
│   │   └── events
│   │       ├── css
│   │       ├── fonts
│   │       ├── img
│   │       └── js

Statische Dateien in Templates laden

In den Templates können die statischen Dateien jetzt «dynamisch» eingebunden werden. Dazu lädt man das {% static %} Template-Tag. Dies wird am besten ziemlich weit oben im Template geladen, aber nach {% extends %}. Das sieht folgendermassen aus:

<!-- mein_projekt/events/templates/events/events_home.html -->

{% extends "base.html" %}
{% load static %}
<img src="{% static "events/img/event-logo.jpg" %}" alt="Event Logo">

Als erstes laden wir mit {% load static %} das Template-Tag für die Verwaltung der statischen Dateien in das Template und binden dann mit diesem Tag die statischen Dateien in unsere Webseite ein: {% static "events/img/event-logo.jpg" %}. Wichtig ist hier die Angabe events am Anfang des Pfades ("events/img/event-logo.jpg"), damit Django weiss, in welchem Namensraum (sprich in welcher App) nachgeschaut werden soll.

Globales Verzeichnis für projektweite Dateien festlegen

Bei einem Webprojekt gibt es immer Dateien, die im ganzen Projekt vorkommen und nicht zu einer bestimmten App gehören. Das sind z.B. die Logos der Webseite, Hintergrundbilder oder globale JavaScript-Dateien. Um diese ebenfalls mit der staticfiles-App von Django verwalten zu können, muss folgender Eintrag in settings.py vorgenommen werden:

STATICFILES_DIRS = [BASE_DIR / «static»]

Hinweis: Folgende Anleitung bezieht sich auf Django 3.1 und höher. In diesen Versionen benutzt Django pathlib für die Dateiverwaltung. Bei Versionen kleiner als 3.1 wird statt dessen STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] verwendet.

Auf der Hauptebene des Projektes, das ist dort, wo die Datei manage.py liegt, legt man ein Verzeichnis namens static an:

mkdir static

Das sieht dann so aus:

mein_projekt
├── events
├── db.sqlite3
├── manage.py
├── mein_projekt
├── static # NEUES VERZEICHNIS STATIC
└── templates

Auch dieses Verzeichnis unterteilt man der Ordnung halber am besten nochmals in vier Unterverzeichnisse img, js, css und fonts:

static
├── css
├── fonts
├── img
└── js

Im Template werden die Dateien dann so eingebunden:

<!-- mein_projekt/templates/base.html -->

<!-- Laden des static template tags -->
{% load static %}
<!DOCTYPE html>
<html>
<head>
  <title>Mein Django-Projekt</title>
  <!-- Einbinden der statischen Datei mit dem static tag -->
  <link rel="stylesheet" href="{% static "css/base.css" %}">
</head>
...
</html>

Hier ist im Pfad zur statischen Datei keine Angabe des Namensraums nötig, die Angabe des Unterordners, in diesem Fall "css/base.css" reicht.

Statische Dateien während der Entwicklung ausliefern

Mit den obigen Einstellungen ist man für die Zeit während der Entwicklung gut versorgt. Der in Django eingebaute runserver sucht im Debbug-Modus automatisch in jeder App die benötigen Dateien und liefert sie aus. Sobald die Webseite auf einem Live-Server mit einem Webserver wie Apache oder nginx ausgeliefert wird, müssen ein paar zusätzliche Einstellungen vorgenommen werden.

Statische Dateien in der Produktion verwalten

Sobald das Projekt auf einem Produktionsserver aufgespielt ist, reicht obige Strategie nicht mehr aus. Die Dateien jedes Mal aus den einzelnen Unterverzeichnissen der Apps zusammenzusuchen wäre langsam und unsicher. In diesem Fall muss ein Verzeichnis bestimmt werden, das für die statischen Dateien in der Produktion benutzt wird. Dieses liegt für kleine Web-Projekte meist auf dem gleichen Server wie die Django-Installation. Den genauen Ort dafür wird in der Server-Konfiguration festgelegt. Dieses Vorgehen reicht für Webseiten mit kleinem bis mittlerem Netzwerk-Verkehr völlig aus. Bei grossen Projekten mit vielen Netzwerkanfragen empfiehlt es sich, die statischen Dateien über ein CDN oder einen spezialisierten File-Server auszuliefern. In diesem Blog-Post benutzen wir die einfache Variante und erstellen das neue Verzeichnis namens staticfiles direkt neben dem Verzeichnis static.

Um Django mitzuteilen, wo sich dieser Ordner befindet, fügen wir in settings.py dafür folgende globale Variable hinzu:

STATIC_ROOT = BASE_DIR / 'staticfiles'

Sobald das Projekt auf dem Webserver installiert ist, müssen alle statischen Dateien in diesem Verzeichnis gesammelt werden. Dazu führt man folgenden Befehl aus:

python manage.py collectstatic

Damit werden Dateien aus den einzelnen Apps in dieses Sammelverzeichnis kopiert. Ist das Verzeichnis noch nicht erstellt, erledigt das Django jetzt. Das beinhaltet alle statischen Dateien aus vorinstallierten Django-Apps wie django.contrib.admin und Drittparteien Apps sowie unsere eigenen Apps. Dieser Befehl muss nach jeder Änderung an den statischen Dateien erneut ausgeführt werden. Und auch, wenn eine neue App zum Projekt hinzugefügt wird.

WhiteNoise

Um das Ausliefern der statischen Dateien in der Produktion weiter zu konfigurieren, bietet die App django.contrib.staticfiles bereits einige Möglichkeiten. Mit der App WhiteNoise, kann dies aber noch erheblich verbessert werden. So bietet WhiteNoise die Möglichkeit die statischen Dateien komprimiert, z.B. als gzip, auszuliefern und die Cache-Header zu konfigurieren. Mehr Informationen zur App gibt es hier.

Zusammenfassung

Die wichtigsten Punkte auf einen Blick:

  • Django verwaltet statische Dateien mit der App django.contrib.staticfiles.
  • Dateien werden für jede App in einem Verzeichnis mit folgendem Pfad abgelegt: meine_app/static/meine_app/...
  • Statische Dateien für die Webseite werden im Verzeichnis static auf der Hauptebene des Projektes abgelegt
  • Die Konfiguration findet in settings.py statt
  • Der Django runserver liefert während der Entwicklung alle statischen Dateien direkt aus den jeweiligen Verzeichnissen aus.
  • Für ein Live-Projekt auf einem Webserver muss STATIC_ROOT konfiguriert werden.
  • Vor dem ersten Deployment und nach jeder Änderung an den statischen Dateien (oder Installation einer neuen App) muss collectstatic ausgeführt werden.
  • Weitere Einstellmöglichkeiten für statische Dateien bietet die App WhiteNoise

 

Das könnte dich auch interessieren

Erstellen eines Django-Projektes

  • 30. Januar 2021

Ein Django-Projekt zu erstellen ist nicht schwer und schnell erledigt. Dennoch besteht die Installation aus mehreren Schritten. In diesem Blog-Post habe ich diese festgehalten. Auf diese Weise kann ich ein neues Projekt schnell aufsetzen und mit der Arbeit an der eigentlichen Idee beginnen.

Django-Superkraft: Eine CRUD-Web-App in 60 Minuten

Teil 1: Installation und Konfiguration

  • 17. Juli 2020

Eine der Stärken von Django ist, dass man schnell zu präsentierbaren Ergebnissen gelangt. Ein Beispiel dafür ist die Arbeit mit einer Datenbank, was für eine moderne Web-App zentral ist. Bei vielen Systemen besteht die Herausforderung darin, zuerst die aufwendige Konfiguration des Datenbanksystems und der API richtig hinzubekommen, bevor überhaupt mit der Programmierung der Web-App begonnen werden kann. Nicht so in Django. Die Anbindung an eine Datenbank ist ein Kinderspiel. Dies zeige ich am Beispiel dieses Tutorials, in dem wir eine voll funktionsfähige Web-App mit Datenbankanbindung erstellen.

Django-Superkraft: Eine CRUD-Web-App in 60 Minuten

Teil 2: Erstellen des Front-End

  • 19. September 2020

Eine der Stärken von Django ist, dass man schnell zu präsentierbaren Ergebnissen gelangt. In diesem zweiteiligen Tutorial zeige ich auf, wie man in knapp 60 Minuten eine CRUD-App erstellt. Im ersten Teil haben wir das Projekt installiert und konfiguriert, die App initialisiert, die Datenbankmodelle hinzugefügt und mit dem Django-Admin verknüpft. In diesem Teil des Tutorials werden wir ein Front-End hinzufügen, das alle vier CRUD-Operationen unterstützt, plus eine Listenansicht der Datenbankeinträge.