Django: ¿cómo crear un archivo y guardarlo en el FileField de un modelo?

110

Aquí está mi modelo. Lo que quiero hacer es generar un nuevo archivo y sobrescribir el existente cada vez que se guarda una instancia de modelo:

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)

Veo mucha documentación sobre cómo cargar un archivo. Pero, ¿cómo puedo generar un archivo, asignarlo a un campo de modelo y hacer que Django lo almacene en el lugar correcto?

Greg
fuente

Respuestas:

152

Desea echar un vistazo a FileField y FieldFile en los documentos de Django, y especialmente FieldFile.save () .

Básicamente, un campo declarado como FileField, cuando se accede a él, le da una instancia de clase FieldFile, que le brinda varios métodos para interactuar con el archivo subyacente. Entonces, lo que debes hacer es:

self.license_file.save(new_name, new_contents)

donde new_namees el nombre de archivo que desea asignar y new_contentses el contenido del archivo. Tenga en cuenta que new_contentsdebe ser una instancia de django.core.files.Fileo django.core.files.base.ContentFile(consulte los enlaces dados al manual para obtener más detalles). Las dos opciones se reducen 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'))
tawmas
fuente
1
Ok, creo que funcionará, pero estoy entrando en una especie de bucle recursivo que lo llama en el método de guardar. Sigue creando archivos para siempre.
Greg
11
Para el problema recursivo, tengo que llamar a self.license_file.save con arg save = False.
Greg
1
Este (ContentFile) funciona perfectamente con la cadena de archivo devuelta por el convert_to_pdfcomando django-wkhtmltopdf . ¡¡Gracias!!
Nostalg.io
Además de esto, recibo un error si no especifico el modo de archivo al abrir el archivo. Por lo tanto, f = open('/path/to/file', 'r')para Zip tipo de archivo,f = open('/path/to/file.zip', 'rb')
rajagopalx
1
En mi caso, lo anterior no fue guardar el archivo en la carpeta. Resulta que el problema es que estoy usando docker-compose para ejecutar mi aplicación django junto con un trabajador de apio. El volumen de la aplicación django para MEDIA_ROOTno se compartió con el mismo volumen en el trabajador de apio. Compartir el volumen nombrado lo solucionó ( ref ).
shadi
28

La respuesta aceptada es ciertamente una buena solución, pero esta es la forma en que genere un CSV y lo sirvo desde una vista.

Pensé que valía la pena poner esto aquí, ya que me tomó un poco de manipulación para obtener todo el comportamiento deseable (sobrescribir el archivo existente, almacenarlo en el lugar correcto, no crear archivos duplicados, etc.).

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
Markdsievers
fuente
1

Es una buena práctica utilizar un administrador de contexto o llamar close()en caso de excepciones durante el proceso de guardado del archivo. Podría suceder si su backend de almacenamiento no funciona, etc.

Cualquier comportamiento de sobrescritura debe configurarse en su backend de almacenamiento. Por ejemplo, S3Boto3Storage tiene una configuración AWS_S3_FILE_OVERWRITE. Si está utilizando FileSystemStorage, puede escribir un mixin personalizado .

También es posible que desee llamar al método de guardado del modelo en lugar del método de guardado de FileField si desea que ocurran efectos secundarios personalizados, como las marcas de tiempo actualizadas por última vez. Si ese es el caso, también puede establecer el atributo de nombre del archivo en el nombre del archivo, que es relativo a MEDIA_ROOT. Tiene como valor predeterminado la ruta completa del archivo, lo que puede causar problemas si no lo configura; consulte Archivo .__ init __ () y File.name .

Aquí hay un ejemplo donde selfestá la instancia del modelo donde my_fileestá FileField / ImageFile, llamando save()a toda la instancia del modelo en lugar de solo a 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()
whp
fuente