2011-11-30 12 views
62

Ho un file esistente su disco (ad esempio /folder/file.txt) e un campo Modello FileField in Django.Imposta il FileField di Django su un file esistente

Quando faccio

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

ri-salva il file come file_1.txt (la prossima volta è _2, etc.).

Capisco perché, ma non voglio questo comportamento - so che il file con cui voglio che il campo sia associato è davvero lì ad aspettarmi, e voglio solo che Django lo indichi.

Come?

+1

sicuro che si può ottenere quello che vuoi senza modificare Django o sottoclasse 'FileField'. Ogni volta che un 'FileField' viene salvato, viene creata una nuova copia del file. Sarebbe abbastanza semplice aggiungere un'opzione per evitare questo. –

+0

beh, sì, sembra che devo sottoclasse e aggiungere un param. Non desidero creare tabelle aggiuntive per questa semplice attività – Guard

+0

Metti il ​​file in una posizione diversa, crea il tuo campo con questo percorso, salvalo e poi hai il file nella destinazione upload_to. – benjaoming

risposta

18

Se si vuole fare questo in modo permanente, è necessario creare il proprio Classe FileStorage

from django.core.files.storage import FileSystemStorage 

class MyFileStorage(FileSystemStorage): 

    # This method is actually defined in Storage 
    def get_available_name(self, name): 
     return name # simply returns the name passed 

Ora nel modello, si utilizza il tuo MyFileStorage modificato

from mystuff.customs import MyFileStorage 

mfs = MyFileStorage() 

class SomeModel(model.Model): 
    my_file = model.FileField(storage=mfs) 
+0

oh, sembra promettente. Il codice di FileField è un po 'non intuitivo – Guard

+0

ma ... è possibile cambiare lo spazio di archiviazione per ogni richiesta, come: instance.field.storage = mfs; instance.field.save (nome, file); ma non farlo in un ramo diverso del mio codice – Guard

+2

No, poiché il motore di archiviazione è legato al modello. È possibile evitare tutto ciò semplicemente memorizzando il percorso del file in un "FilePathField" o semplicemente come testo normale. –

0

Ho avuto esattamente lo stesso problema! poi mi rendo conto che i miei modelli lo stavano causando. esempio ho hade miei modelli come questo:

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

Poi, ho voluto avere più l'una piastrella riferimento lo stesso file nel disco! Il modo che ho trovato per risolvere questo era cambiare la mia struttura di modello a questo:

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

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

Che dopo mi rendo conto che più senso, perché se voglio lo stesso file viene salvato più di uno nella mia DB devo crea un altro tavolo per questo!

Immagino che tu possa risolvere il tuo problema in questo modo, sperando che tu possa cambiare i modelli!

EDIT

Inoltre Credo che si può utilizzare uno storage diversa, in questo modo, per esempio: SymlinkOrCopyStorage

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

+0

ha senso nel tuo caso, non nel mio. Non voglio che venga referenziato più volte. Creo un oggetto che fa riferimento a un file, quindi mi rendo conto che ci sono errori in altri attrs e riapro il modulo di creazione. Nella sua nuova presentazione non voglio perdere il file che è già stato salvato sul disco – Guard

+0

quindi immagino che tu possa usare il mio approccio! perché avrai una tabella FormFile che manterrà il file solo dopo averlo! quindi nella tua tabella Form avrai un FK per quel file! così puoi cambiare/creare nuove forme per lo stesso file! (btw sto cambiando l'ordine del FK nel mio esempio principale) –

+0

Se vuoi pubblicare il tuo dominio (modelli) nel tuo post! posso avere anche una ideia migliore! –

4

È giusto scrivere la propria classe di archiviazione. Tuttavia, get_available_name non è il metodo giusto per eseguire l'override.

get_available_name viene chiamato quando Django vede un file con lo stesso nome e tenta di ottenere un nuovo nome di file disponibile. Non è il metodo che causa la rinomina. il metodo ha causato che è _save. I commenti in _save sono abbastanza buoni e puoi facilmente trovarlo per aprire il file per la scrittura con flag os.O_EXCL che genererà un OSError se lo stesso nome file esiste già. Django rileva questo errore quindi chiama get_available_name per ottenere un nuovo nome.

Quindi penso che il modo corretto sia di ignorare _save e chiamare os.open() senza bandiera os.O_EXCL. La modifica è abbastanza semplice, tuttavia il metodo è un po 'lungo, quindi non lo incollo qui.Dimmi se avete bisogno di più aiuto :)

+0

ci sono 50 righe di codice che devi copiare, il che è piuttosto brutto. Sovrascrivere get_available_name sembra più isolato, più breve e molto più sicuro per, per esempio, l'aggiornamento alle versioni più recenti di Django in futuro –

+1

Il problema di * only * sovrascrivere 'get_available_name' è quando si carica un file con lo stesso nome, il server entrare in un ciclo infinito. Poiché '_save' controlla il nome del file e decide di ottenerne uno nuovo tuttavia 'get_available_name' restituisce ancora il duplicato. Quindi devi scavalcare entrambi. – x1a0

+1

Oops, stiamo facendo questa discussione in due domande, ma solo ora ho notato che sono leggermente diverse) Quindi ho ragione in questa domanda, e tu ci sei dentro) –

83

solo impostare instance.field.name al percorso del file

esempio

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> 
+11

Il percorso relativo dal tuo 'MEDIA_ROOT', che è. – mgalgs

+6

In questo esempio, penso che si possa anche solo fare 'doc.file = 'path/to/file'' –

7

provare questo (doc):

instance.field.name = <PATH RELATIVE TO MEDIA_ROOT> 
instance.save() 
Non
Problemi correlati