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 () .

В основному, поле, оголошене як 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
Гаразд, я думаю, що це спрацює, але я потрапляю в якийсь рекурсивний цикл, який викликає це в методі збереження. Він просто продовжує створювати файли назавжди.
Грег

11
Для рекурсивної проблеми я повинен зателефонувати self.license_file.save з аргументом save = False.
Грег

1
Це (ContentFile) прекрасно працює з файловим рядком, повернутим командою django-wkhtmltopdf convert_to_pdf. Дякую!!
Nostalg.io

На додаток до цього я отримав помилку, якщо під час відкриття файлу не вказую режим файлу. Отже, f = open('/path/to/file', 'r')для файлу ZIP-роду,f = open('/path/to/file.zip', 'rb')
rajagopalx

1
У моєму випадку вищезгадане не було збереженням файлу у папку. Виявляється, проблема полягає в тому, що я використовую докер-композит, щоб запустити додаток джанго разом із працівником селери. Об'єм програми для джанго для спілера MEDIA_ROOTз селерою не ділився однаковим. Спільний доступ до названого тому виправив ( ref ).
шаді

28

Прийнята відповідь, безумовно, хороше рішення, але ось я пішов шляхом створення CSV та обслуговування його з точки зору.

Думав, що варто поставити це сюди, оскільки мені знадобилося трохи хизуватися, щоб отримати всі бажані дії (перезаписати існуючий файл, зберігати в потрібному місці, не створювати дублікатів файлів тощо).

Джанго 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

Доброю практикою є використання контекстного менеджера або дзвінка close()у випадку винятків під час збереження файлів. Це може статися, якщо ваш сервер пам’яті не працює тощо.

Будь-яка поведінка перезапису повинна бути налаштована у вашому резервному інтервалі пам’яті. Наприклад, S3Boto3Storage має налаштування AWS_S3_FILE_OVERWRITE. Якщо ви використовуєте, FileSystemStorageви можете написати спеціальний mixin .

Ви також можете зателефонувати за методом збереження моделі замість методу збереження FileField, якщо ви хочете, щоб відбулися будь-які користувацькі побічні ефекти, як-от останні оновлені часові позначки. Якщо це так, ви також можете встановити атрибут імені файлу на ім'я файлу - що відносно MEDIA_ROOT. Він за замовчуванням використовує повний шлях до файлу, який може спричинити проблеми, якщо ви не встановите його - див. Файл .__ init __ () та File.name .

Ось приклад, де selfзнаходиться екземпляр моделі, де my_fileзнаходиться FileField / 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()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.