Überschreiben hochgeladener Dateien
Wird eine Datei für ein FileField oder ImageField hochgeladen und exisitiert bereits, überschreibt Django diese Datei nicht sondern hängt einen Tiefstrich an die Datei. Die ältere Datei bleibt ohne Verweis im Dateisystem bestehen.Überschreibst du also eine Datei rechnung.pdf drei mal, entsteht eine lustige Kaskade im Dateisystem.
- rechnung.pdf
- rechnung_.pdf
- rechnung__.pdf
- rechnung___.pdf (Der Pfad zu dieser Datei wird im FileField gespeichert)
Es ist nicht immer im Interesse, diese alten Dateien zu behalten. Sei es aus Platzmangel, Datenschutzgründen oder weil man einfach nur Ordnung auf seinem System halten will.
Derzeit gibt es keinen direkten Weg, das überschreiben zu erzwingen. Es gibt schon seit längerer Zeit ein Ticket im Django-Repository aber es schaut nicht so aus, als ob es in Django 1.1 eingespielt wird.
Automatisches Überschreiben mit einem Custom-Storage
Hochgeladene Dateien werden von einem eigenen Modul, dem FileSystemStorage verarbeitet. Es öffnet und speichert die Dateien und prüft, ob der Dateiname schon exisitiert und hängt ggf. einen Tiefstrich an die Datei. Das passiert in der Methode get_available_name. Genau diese ist der Ansatzpunkt um das Überschreiben zu erzwingen.
Hier wird ein eigenes Storage erstellt aber vor dem Speichern der Datei, wird (wenn sie existiert) die alte Datei gelöscht:
from django.db import models
from django.core.files.storage import FileSystemStorage
class OverwriteFileSystemStorage(FileSystemStorage):
def get_available_name(self, name):
if self.exists(name):
self.delete(name)
return name
class Invoice(models.Model):
title = models.CharField(max_length=120)
invoice = models.FileField(upload_to=upload_invoice, storage=OverwriteFileSystemStorage())
def __unicode__(self):
return self.title
Das Custom-Storage wird dem FileField oder dem ImageField als Attribut storage übergeben. Soll dieses Schema für alle Uploads gelten, ändere in der settings.py den Wert DEFAULT_FILE_STORAGE Standardmässig wird das oben genannte FileSystemStorage verwendet.
# Original
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
# Immer alle Dateien überschreiben
DEFAULT_FILE_STORAGE = 'path.to.OverwriteFileSystemStorage'
Den originalen Dateinamen zusätzlich speichern
Dass Dateien nicht grundsätzlich überschrieben werden ist ja grundsätzlich keine schlechte Idee. Schließlich hat man immer ein automatisches Backup aller Änderungen. Lassen wir das Schema so wie es ist aber speichern wir den originalen Dateinamen zusätzlich in einem eigenen Feld. So können wir dem User später den orignalen Dateinamen anzeigen, im Dateisystem bleiben aber alle überschriebenen Dateien erhalten.
Ein upload-Callable ist ein schneller und sicherer Weg, dies zu erreichen. Mehr dazu hatte ich schon in Ordnung im Medienordner geschrieben.
from django.db import models
class Invoice(models.Model):
def upload_invoice(instance, filename):
instance.invoice_orig_filename = filename
return filename
title = models.CharField(max_length=120)
invoice = models.FileField(upload_to=upload_invoice, storage=OverwriteFileSystemStorage())
invoice_orig_filename = models.CharField(max_length=100, blank=True)
def __unicode__(self):
return self.title
Dateien löschen

Der Vollständigkeit halber: Von Haus aus ist es im Admin noch nicht möglich, hochgeladene Dateien wieder zu löschen. Stephan Jäkel hat ein Admin-Widget konstruiert, dass Dateien auf Klick wieder löscht.