Django: come creare un file e salvarlo nel FileField di un modello?


110

Ecco il mio modello. Quello che voglio fare è generare un nuovo file e sovrascrivere quello esistente ogni volta che viene salvata un'istanza del modello:

class Kitten(models.Model):
    claw_size = ...
    license_file = models.FileField(blank=True, upload_to='license')

    def save(self, *args, **kwargs):
        #Generate a new license file overwriting any previous version
        #and update file path
        self.license_file = ???
        super(Request,self).save(*args, **kwargs)

Vedo molta documentazione su come caricare un file. Ma come posso generare un file, assegnarlo a un campo modello e fare in modo che Django lo memorizzi nel posto giusto?

Risposte:


152

Vuoi dare un'occhiata a FileField e FieldFile nei documenti Django, e specialmente FieldFile.save () .

Fondamentalmente, un campo dichiarato come a FileField, quando vi si accede, fornisce un'istanza di classe FieldFile, che fornisce diversi metodi per interagire con il file sottostante. Quindi, quello che devi fare è:

self.license_file.save(new_name, new_contents)

dove new_nameè il nome del file che si desidera assegnare ed new_contentsè il contenuto del file. Si noti che new_contentsdeve essere un'istanza di django.core.files.Fileo django.core.files.base.ContentFile(vedere i collegamenti forniti al manuale per i dettagli). Le due scelte si riducono a:

# Using File
f = open('/path/to/file')
self.license_file.save(new_name, File(f))
# Using ContentFile
self.license_file.save(new_name, ContentFile('A string with the file content'))

1
Ok, penso che funzionerà ma sto entrando in una sorta di ciclo ricorsivo che lo chiama nel metodo di salvataggio. Continua a creare file per sempre.
Greg

11
Per il problema ricorsivo devo chiamare self.license_file.save con arg save = False.
Greg

1
Questo (ContentFile) funziona perfettamente con la stringa del file restituita dal convert_to_pdfcomando di django-wkhtmltopdf . Grazie!!
Nostalg.io

Inoltre, ho ricevuto un errore se non ho specificato la modalità file durante l'apertura del file. Quindi, f = open('/path/to/file', 'r')per il tipo di file ZIP,f = open('/path/to/file.zip', 'rb')
rajagopalx

1
Nel mio caso, quanto sopra non stava salvando il file nella cartella. Si scopre che il problema è che sto usando docker-compose per eseguire la mia app django insieme a un lavoratore sedano. Il volume dell'app django per MEDIA_ROOTnon è stato condiviso con lo stesso volume in celery worker. La condivisione del volume denominato lo ha risolto ( ref ).
shadi

28

La risposta accettata è sicuramente una buona soluzione, ma ecco il modo in cui sono andato per generare un CSV e servirlo da una vista.

Ho pensato che valesse la pena metterlo qui perché mi ci è voluto un po 'di tempo per ottenere tutto il comportamento desiderabile (sovrascrivere il file esistente, archiviarlo nel punto giusto, non creare file duplicati, ecc.).

Django 1.4.1

Python 2.7.3

#Model
class MonthEnd(models.Model):
    report = models.FileField(db_index=True, upload_to='not_used')

import csv
from os.path import join

#build and store the file
def write_csv():
    path = join(settings.MEDIA_ROOT, 'files', 'month_end', 'report.csv')
    f = open(path, "w+b")

    #wipe the existing content
    f.truncate()

    csv_writer = csv.writer(f)
    csv_writer.writerow(('col1'))

    for num in range(3):
        csv_writer.writerow((num, ))

    month_end_file = MonthEnd()
    month_end_file.report.name = path
    month_end_file.save()

from my_app.models import MonthEnd

#serve it up as a download
def get_report(request):
    month_end = MonthEnd.objects.get(file_criteria=criteria)

    response = HttpResponse(month_end.report, content_type='text/plain')
    response['Content-Disposition'] = 'attachment; filename=report.csv'

    return response

1

È buona norma utilizzare un gestore di contesto o chiamare close()in caso di eccezioni durante il processo di salvataggio del file. Potrebbe accadere se il tuo backend di archiviazione è inattivo, ecc.

Qualsiasi comportamento di sovrascrittura dovrebbe essere configurato nel back-end di archiviazione. Ad esempio S3Boto3Storage ha un'impostazione AWS_S3_FILE_OVERWRITE. Se stai usando FileSystemStoragepuoi scrivere un mixin personalizzato .

Potresti anche voler chiamare il metodo di salvataggio del modello invece del metodo di salvataggio di FileField se desideri che si verifichino effetti collaterali personalizzati, come i timestamp dell'ultimo aggiornamento. In tal caso, puoi anche impostare l'attributo name del file sul nome del file, che è relativo a MEDIA_ROOT. Il valore predefinito è il percorso completo del file che può causare problemi se non lo si imposta - vedere File .__ init __ () e File.name .

Ecco un esempio in cui si selftrova l'istanza del modello in cui si my_filetrova FileField / ImageFile, chiamando save()l'intera istanza del modello anziché solo FileField:

import os
from django.core.files import File

with open(filepath, 'rb') as fi:
    self.my_file = File(fi, name=os.path.basename(fi.name))
    self.save()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.