Django - làm thế nào để tạo một tệp và lưu nó vào FileField của một mô hình?


109

Đây là mô hình của tôi. Những gì tôi muốn làm là tạo một tệp mới và ghi đè lên tệp hiện có bất cứ khi nào một cá thể mô hình được lưu:

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)

Tôi thấy rất nhiều tài liệu về cách tải tệp lên. Nhưng làm cách nào để tạo tệp, gán tệp đó vào trường mô hình và để Django lưu trữ tệp đó vào đúng vị trí?

Câu trả lời:


151

Bạn muốn xem FileField và FieldFile trong tài liệu Django và đặc biệt là FieldFile.save () .

Về cơ bản, một trường được khai báo là FileField, khi được truy cập, sẽ cung cấp cho bạn một thể hiện của lớp FieldFile, cung cấp cho bạn một số phương thức để tương tác với tệp bên dưới. Vì vậy, những gì bạn cần làm là:

self.license_file.save(new_name, new_contents)

đâu new_namelà tên tệp bạn muốn gán và new_contentslà nội dung của tệp. Lưu ý rằng đó new_contentsphải là một phiên bản của một trong hai django.core.files.Filehoặc django.core.files.base.ContentFile(xem các liên kết nhất định đến hướng dẫn sử dụng để biết chi tiết). Hai lựa chọn tổng hợp thành:

# 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
Được rồi, tôi nghĩ điều đó sẽ hiệu quả nhưng tôi đang vướng vào một số loại vòng lặp đệ quy gọi điều đó trong phương thức lưu. Nó chỉ tiếp tục tạo tệp mãi mãi.
Greg

11
Đối với vấn đề đệ quy, tôi phải gọi self.license_file.save với arg save = False.
Greg

1
(ContentFile) này hoạt động hoàn hảo với chuỗi tệp được trả về bởi convert_to_pdflệnh của django-wkhtmltopdf . Cảm ơn bạn!!
Nostalg.io

Ngoài ra, tôi gặp lỗi nếu tôi không chỉ định chế độ tệp trong khi mở tệp. Vì vậy, f = open('/path/to/file', 'r')Đối với bưu điện loại tập tin,f = open('/path/to/file.zip', 'rb')
rajagopalx

1
Trong trường hợp của tôi, cách trên không lưu tệp vào thư mục. Hóa ra vấn đề là tôi đang sử dụng docker -omp để chạy ứng dụng django của mình cùng với một nhân viên cần tây. Âm lượng ứng dụng django cho MEDIA_ROOTkhông được chia sẻ với cùng một âm lượng trong celery worker. Chia sẻ tập đã đặt tên đã sửa nó ( ref ).
shadi

28

Câu trả lời được chấp nhận chắc chắn là một giải pháp tốt, nhưng đây là cách tôi đã tạo ra một CSV và phân phát nó từ một chế độ xem.

Tôi nghĩ rằng nó đáng giá khi đặt điều này ở đây vì tôi đã phải mất một chút thời gian để có được tất cả các hành vi mong muốn (ghi đè lên tệp hiện có, lưu trữ vào đúng vị trí, không tạo tệp trùng lặp, v.v.).

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

Bạn nên sử dụng trình quản lý ngữ cảnh hoặc cuộc gọi close()trong trường hợp ngoại lệ trong quá trình lưu tệp. Có thể xảy ra nếu chương trình phụ trợ lưu trữ của bạn bị lỗi, v.v.

Mọi hành vi ghi đè phải được định cấu hình trong phần phụ trợ lưu trữ của bạn. Ví dụ S3Boto3Storage có một cài đặt AWS_S3_FILE_OVERWRITE. Nếu bạn đang sử dụng, FileSystemStoragebạn có thể viết một mixin tùy chỉnh .

Bạn cũng có thể muốn gọi phương thức lưu của mô hình thay vì phương thức lưu của FileField nếu bạn muốn bất kỳ hiệu ứng phụ tùy chỉnh nào xảy ra, như dấu thời gian cập nhật lần cuối. Nếu đúng như vậy, bạn cũng có thể đặt thuộc tính name của tệp thành tên của tệp - có liên quan đến MEDIA_ROOT. Nó mặc định là đường dẫn đầy đủ của tệp có thể gây ra sự cố nếu bạn không đặt nó - xem File .__ init __ ()File.name .

Đây là một ví dụ trong đó selflà cá thể mô hình trong đó my_filelà FileField / ImageFile, gọi save()trên toàn bộ cá thể mô hình thay vì chỉ 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()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.