Django - จะสร้างไฟล์และบันทึกลงใน FileField ของโมเดลได้อย่างไร


110

นี่คือโมเดลของฉัน สิ่งที่ฉันต้องการทำคือสร้างไฟล์ใหม่และเขียนทับไฟล์ที่มีอยู่ทุกครั้งที่บันทึกอินสแตนซ์โมเดล:

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)

ฉันเห็นเอกสารมากมายเกี่ยวกับวิธีอัปโหลดไฟล์ แต่ฉันจะสร้างไฟล์กำหนดให้กับฟิลด์โมเดลและให้ Django จัดเก็บในตำแหน่งที่ถูกต้องได้อย่างไร

คำตอบ:


152

คุณต้องการที่จะมีลักษณะที่FileField และ FieldFileในเอกสาร Django และโดยเฉพาะอย่างยิ่งFieldFile.save ()

โดยพื้นฐานแล้วฟิลด์ที่ประกาศเป็น a FileFieldเมื่อเข้าถึงจะให้อินสแตนซ์ของคลาสFieldFileซึ่งช่วยให้คุณมีวิธีการต่างๆในการโต้ตอบกับไฟล์ต้นแบบ ดังนั้นสิ่งที่คุณต้องทำคือ:

self.license_file.save(new_name, new_contents)

new_nameชื่อไฟล์ที่คุณต้องการกำหนดอยู่ที่ไหนและnew_contentsเป็นเนื้อหาของไฟล์ โปรดทราบว่าnew_contentsต้องเป็นอินสแตนซ์ของอย่างใดอย่างหนึ่งdjango.core.files.Fileหรือdjango.core.files.base.ContentFile(ดูรายละเอียดในลิงก์ไปยังคู่มือ) สองตัวเลือกต้มลงไปที่:

# 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
โอเคฉันคิดว่าจะได้ผล แต่ฉันกำลังเข้าสู่การเรียกวนซ้ำแบบวนซ้ำแบบนั้นในวิธีบันทึก เพียงแค่สร้างไฟล์ตลอดไป
Greg

11
สำหรับปัญหาการเรียกซ้ำฉันต้องเรียก self.license_file.save ด้วย arg save = False
Greg

1
(ContentFile) นี้ทำงานได้อย่างสมบูรณ์กับสตริงไฟล์ที่ส่งคืนโดยconvert_to_pdfคำสั่งของ django-wkhtmltopdf ขอบคุณ!!
Nostalg.io

นอกจากนี้ฉันยังพบข้อผิดพลาดหากไม่ระบุโหมดไฟล์ขณะเปิดไฟล์ ดังนั้นf = open('/path/to/file', 'r')สำหรับไฟล์ประเภท ZIPf = open('/path/to/file.zip', 'rb')
rajagopalx

1
ในกรณีของฉันข้างต้นไม่ได้บันทึกไฟล์ลงในโฟลเดอร์ ปรากฎว่าปัญหาคือฉันใช้นักเทียบท่าเขียนเพื่อเรียกใช้แอป django พร้อมกับคนงานขึ้นฉ่าย ปริมาณแอป django สำหรับMEDIA_ROOTไม่ได้ใช้ร่วมกับระดับเสียงเดียวกันในคนงานขึ้นฉ่าย การแบ่งปันไดรฟ์ข้อมูลที่ตั้งชื่อได้รับการแก้ไขแล้ว ( อ้างอิง )
เฉด

28

คำตอบที่ได้รับการยอมรับเป็นทางออกที่ดีอย่างแน่นอน แต่นี่คือวิธีที่ฉันทำเกี่ยวกับการสร้าง CSV และให้บริการจากมุมมอง

คิดว่ามันคุ้มค่าที่จะวางสิ่งนี้ไว้ที่นี่เพราะฉันใช้เวลาเล่นซอเล็กน้อยเพื่อให้ได้พฤติกรรมที่ต้องการทั้งหมด (เขียนทับไฟล์ที่มีอยู่จัดเก็บในจุดที่ถูกต้องไม่สร้างไฟล์ที่ซ้ำกัน ฯลฯ )

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

2

เป็นแนวทางปฏิบัติที่ดีในการใช้ตัวจัดการบริบทหรือการโทรclose()ในกรณีที่มีข้อยกเว้นในระหว่างกระบวนการบันทึกไฟล์ อาจเกิดขึ้นหากแบ็กเอนด์พื้นที่เก็บข้อมูลของคุณไม่ทำงานเป็นต้น

ควรกำหนดค่าพฤติกรรมการเขียนทับใด ๆ ในแบ็กเอนด์พื้นที่เก็บข้อมูลของคุณ ยกตัวอย่างเช่น S3Boto3Storage AWS_S3_FILE_OVERWRITEมีการตั้งค่า หากคุณใช้FileSystemStorageคุณสามารถเขียนมิกซ์อินที่กำหนดเองได้

คุณอาจต้องการเรียกใช้วิธีการบันทึกของโมเดลแทนวิธีการบันทึกของ FileField หากคุณต้องการให้เอฟเฟกต์ข้างเคียงที่กำหนดเองเกิดขึ้นเช่นการประทับเวลาที่อัปเดตล่าสุด หากเป็นเช่นนั้นคุณยังสามารถตั้งค่าแอตทริบิวต์ name ของไฟล์เป็นชื่อของไฟล์ซึ่งสัมพันธ์กับMEDIA_ROOT. ค่าเริ่มต้นเป็นพา ธ แบบเต็มของไฟล์ซึ่งอาจทำให้เกิดปัญหาได้หากคุณไม่ได้ตั้งค่า - ดูไฟล์. __ init __ ()และFile.name File.name

นี่คือตัวอย่างselfอินสแตนซ์โมเดลที่my_fileFileField / ImageFile อยู่ที่ใดโดยเรียกsave()ใช้อินสแตนซ์โมเดลทั้งหมดแทนที่จะเป็นเพียง 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()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.