Setzen Sie Djangos FileField auf eine vorhandene Datei

88

Ich habe eine vorhandene Datei auf der Festplatte (z. B. /folder/file.txt) und ein FileField-Modellfeld in Django.

Wenn ich es tue

instance.field = File(file('/folder/file.txt'))
instance.save()

Die Datei wird erneut gespeichert als file_1.txt(beim nächsten Mal _2usw.).

Ich verstehe warum, aber ich möchte dieses Verhalten nicht - ich weiß, dass die Datei, mit der das Feld verknüpft werden soll, wirklich auf mich wartet, und ich möchte nur, dass Django darauf verweist.

Wie?

Bewachen
quelle
1
Sie sind sich nicht sicher, ob Sie das bekommen können, was Sie wollen, ohne Django oder Unterklassen zu ändern FileField. Immer wenn a FileFieldgespeichert wird, wird eine neue Kopie der Datei erstellt. Es wäre ziemlich einfach, eine Option hinzuzufügen, um dies zu vermeiden.
Michael Mior
Nun ja, es sieht so aus, als müsste ich eine Unterklasse erstellen und einen Parameter hinzufügen. Ich möchte keine zusätzlichen Tabellen für diese einfache Aufgabe erstellen
Guard
1
Legen Sie die Datei an einem anderen Speicherort ab, erstellen Sie Ihr Feld mit diesem Pfad, speichern Sie es und Sie haben die Datei im Ziel upload_to.
Benjaoming

Antworten:

22

Wenn Sie dies dauerhaft tun möchten, müssen Sie Ihre eigene FileStorage-Klasse erstellen

import os
from django.conf import settings
from django.core.files.storage import FileSystemStorage

class MyFileStorage(FileSystemStorage):

    # This method is actually defined in Storage
    def get_available_name(self, name):
        if self.exists(name):
            os.remove(os.path.join(settings.MEDIA_ROOT, name))
        return name # simply returns the name passed

Jetzt verwenden Sie in Ihrem Modell Ihren modifizierten MyFileStorage

from mystuff.customs import MyFileStorage

mfs = MyFileStorage()

class SomeModel(model.Model):
   my_file = model.FileField(storage=mfs)
Burhan Khalid
quelle
Oh, sieht vielversprechend aus. Wenn der Code des FileField nicht intuitiv ist
Guard
aber ... ist es möglich, den Speicher pro Anfrage zu ändern, wie zum Beispiel: instance.field.storage = mfs; instance.field.save (Name, Datei); aber nicht in einem anderen Zweig meines Codes
Guard
2
Nein, da die Speicher-Engine an das Modell gebunden ist. Sie können dies alles vermeiden, indem Sie Ihren Dateipfad entweder in a FilePathFieldoder einfach als einfachen Text speichern .
Burhan Khalid
Sie können nicht einfach einen Namen zurückgeben. Sie müssen zuerst die vorhandene Datei entfernen.
Alexander Shpindler
119

Stellen Sie einfach instance.field.nameden Pfad Ihrer Datei ein

z.B

class Document(models.Model):
    file = FileField(upload_to=get_document_path)
    description = CharField(max_length=100)


doc = Document()
doc.file.name = 'path/to/file'  # must be relative to MEDIA_ROOT
doc.file
<FieldFile: path/to/file>
bara
quelle
15
Der relative Weg von dir MEDIA_ROOT, das heißt.
mgalgs
7
In diesem Beispiel denke ich, dass Sie auch einfach tun könnendoc.file = 'path/to/file'
Andrew Swihart
13

versuchen Sie dies ( doc ):

instance.field.name = <PATH RELATIVE TO MEDIA_ROOT> 
instance.save()
uNmAnNeR
quelle
5

Es ist richtig, eine eigene Speicherklasse zu schreiben. Dies get_available_nameist jedoch nicht die richtige Methode zum Überschreiben.

get_available_namewird aufgerufen, wenn Django eine Datei mit demselben Namen sieht und versucht, einen neuen verfügbaren Dateinamen abzurufen. Es ist nicht die Methode, die das Umbenennen verursacht. die Methode verursacht das ist _save. Kommentare in _saveist ziemlich gut und Sie können leicht feststellen, dass es eine Datei zum Schreiben mit Flag öffnet os.O_EXCL, die einen OSError auslöst, wenn derselbe Dateiname bereits vorhanden ist. Django fängt diesen Fehler ab und ruft get_available_namean, um einen neuen Namen zu erhalten.

Ich denke, der richtige Weg ist, _saveos.open () ohne Flag zu überschreiben und aufzurufen os.O_EXCL. Die Änderung ist recht einfach, aber die Methode ist etwas lang, so dass ich sie hier nicht einfüge. Sag mir, wenn du mehr Hilfe brauchst :)

x1a0
quelle
Es sind 50 Codezeilen, die Sie kopieren müssen, was ziemlich schlecht ist. Das Überschreiben von get_available_name scheint isolierter, kürzer und viel sicherer zu sein, um beispielsweise in Zukunft auf die neueren Versionen von Django zu
aktualisieren
2
Das Problem, nur zu überschreiben, get_available_namebesteht darin, dass der Server beim Hochladen einer Datei mit demselben Namen in eine Endlosschleife gerät. Da _saveder Dateiname überprüft und beschlossen wird, einen neuen zu erhalten, wird get_available_nameder doppelte weiterhin zurückgegeben. Sie müssen also beide überschreiben.
x1a0
1
Hoppla, wir haben diese Diskussion in zwei Fragen, aber erst jetzt habe ich bemerkt, dass sie etwas anders sind.) Also habe ich Recht in dieser Frage, und Sie sind in dieser Frage)
Michael Gendin
1

Ich hatte genau das gleiche Problem! dann merke ich, dass meine Models das verursacht haben. Beispiel Ich hatte meine Modelle so:

class Tile(models.Model):
  image = models.ImageField()

Dann wollte ich mehr die eine Kachel haben, die auf dieselbe Datei auf der Festplatte verweist! Die Art und Weise, wie ich das gelöst habe, war, meine Modellstruktur folgendermaßen zu ändern:

class Tile(models.Model):
  image = models.ForeignKey(TileImage)

class TileImage(models.Model):
  image = models.ImageField()

Was, nachdem ich erkannt habe, dass dies sinnvoller ist, denn wenn ich möchte, dass dieselbe Datei mehr als eine in meiner Datenbank gespeichert wird, muss ich eine andere Tabelle dafür erstellen!

Ich denke, Sie können Ihr Problem auch so lösen, in der Hoffnung, dass Sie die Modelle ändern können!

BEARBEITEN

Ich denke auch, dass Sie einen anderen Speicher verwenden können, wie zum Beispiel: SymlinkOrCopyStorage

http://code.welldev.org/django-storages/src/11bef0c2a410/storages/backends/symlinkorcopy.py

Arthur Neves
quelle
macht in deinem Fall Sinn, nicht in meinem. Ich möchte nicht, dass mehrmals darauf verwiesen wird. Ich erstelle ein Objekt, das auf eine Datei verweist, stelle dann fest, dass Fehler in anderen Attributen vorliegen, und öffne das Erstellungsformular erneut. Bei seiner erneuten Einreichung möchte ich nicht die Datei verlieren, die bereits auf der Festplatte gespeichert ist
Guard
Ich denke, Sie können meinen Ansatz verwenden! weil Sie eine Tabelle FormFile haben, die die Datei nur dann enthält, wenn Sie haben! dann haben Sie in Ihrer Formulartabelle eine FK für diese Datei! Sie können also neue Formulare für dieselbe Datei ändern / erstellen! (Übrigens ändere ich die Reihenfolge der FK in meinem Hauptbeispiel)
Arthur Neves
Wenn Sie Ihre Domain (Modelle) in Ihrem Beitrag veröffentlichen möchten! Ich kann auch eine bessere Idee haben!
Arthur Neves
Die Domain spielt eigentlich keine Rolle - ich habe ein Modell mit einem Foto und einen benutzerdefinierten Bearbeitungsbildschirm. Nach dem Hochladen möchte ich, dass das Foto auf dem Server bleibt, aber ich mag es nicht, ein separates Modell, eine Tabelle und eine FK-Suche zu erstellen, nur weil dies eine Framework-Einschränkung darstellt
Guard
Die Einschränkung hier liegt wohl daran, dass beim Speichern eines FileField in Django immer Django-Speicher durchlaufen werden! Es macht also keinen Sinn, nur einen Dateipfad zu erzwingen! auch woher sollte Django wissen, dass die Datei bereits im Pfad vorhanden ist? Ein anderer Ansatz, den Sie verwenden können, ist die Verwendung von FilePathField! Sie können also einfach den Pfad in Ihrer Datenbank festlegen und die Suche so gestalten, wie Sie es für am besten halten!
Arthur Neves