Django - bagaimana membuat sebuah berkas dan menyimpannya ke FileField model?

110

Ini model saya. Yang ingin saya lakukan adalah membuat file baru dan menimpa yang sudah ada setiap kali contoh model disimpan:

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)

Saya melihat banyak dokumentasi tentang cara mengunggah file. Tetapi bagaimana saya menghasilkan sebuah berkas, menugaskannya ke bidang model dan meminta Django menyimpannya di tempat yang benar?

Greg
sumber

Jawaban:

152

Anda ingin melihat FileField dan FieldFile di dokumen Django, dan terutama FieldFile.save () .

Pada dasarnya, bidang yang dideklarasikan sebagai FileField, saat diakses, memberi Anda instance kelas FieldFile, yang memberi Anda beberapa metode untuk berinteraksi dengan file yang mendasarinya. Jadi, yang perlu Anda lakukan adalah:

self.license_file.save(new_name, new_contents)

di mana new_namenama file yang ingin Anda tetapkan dan new_contentsmerupakan konten file. Perhatikan bahwa new_contentsharus merupakan contoh dari salah satu django.core.files.Fileatau django.core.files.base.ContentFile(lihat tautan yang diberikan ke manual untuk detailnya). Kedua pilihan tersebut bermuara pada:

# 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'))
tawma
sumber
1
Oke, saya pikir itu akan berhasil tetapi saya masuk ke semacam loop rekursif yang memanggilnya dalam metode penyimpanan. Itu hanya membuat file selamanya.
Greg
11
Untuk masalah rekursif saya harus memanggil self.license_file.save dengan argumen save = False.
Greg
1
Ini (ContentFile) bekerja sempurna dengan file string yang dikembalikan oleh convert_to_pdfperintah django-wkhtmltopdf . Terima kasih!!
Nostalg.io
Selain itu, saya mendapat kesalahan jika saya tidak menentukan mode file saat membuka file. Jadi, f = open('/path/to/file', 'r')Untuk jenis file ZIP,f = open('/path/to/file.zip', 'rb')
rajagopalx
1
Dalam kasus saya, hal di atas tidak menyimpan file ke dalam folder. Ternyata masalahnya adalah saya menggunakan buruh pelabuhan-menulis untuk menjalankan aplikasi django saya bersama dengan pekerja seledri. Volume aplikasi django untuk MEDIA_ROOTtidak dibagi dengan volume yang sama di pekerja seledri. Berbagi volume bernama memperbaikinya ( ref ).
shadi
28

Jawaban yang diterima tentu saja merupakan solusi yang baik, tetapi inilah cara saya menghasilkan CSV dan menyajikannya dari tampilan.

Pikir itu layak saat meletakkan ini di sini karena saya perlu sedikit mengutak-atik untuk mendapatkan semua perilaku yang diinginkan (menimpa file yang ada, menyimpan ke tempat yang tepat, tidak membuat file duplikat, dll).

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
sumber
1

Praktik yang baik untuk menggunakan pengelola konteks atau panggilan close()jika terjadi pengecualian selama proses penyimpanan file. Bisa terjadi jika backend penyimpanan Anda tidak aktif, dll.

Setiap perilaku timpa harus dikonfigurasi di backend penyimpanan Anda. Misalnya S3Boto3Storage memiliki pengaturan AWS_S3_FILE_OVERWRITE. Jika Anda menggunakan FileSystemStorageAnda dapat menulis mixin kustom .

Anda mungkin juga ingin memanggil metode penyimpanan model daripada metode penyimpanan FileField jika Anda ingin efek samping kustom terjadi, seperti stempel waktu yang terakhir diperbarui. Jika demikian, Anda juga dapat mengatur atribut nama file ke nama file - yang relatif terhadap MEDIA_ROOT. Ini default ke path lengkap dari file yang dapat menyebabkan masalah jika Anda tidak mengaturnya - lihat File .__ init __ () dan File.name .

Berikut adalah contoh di mana contoh selfmodel di mana my_fileFileField / ImageFile, memanggil save()seluruh contoh model bukan hanya 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
sumber