Django - 如何创建文件并将其保存到模型的 FileField?

Posted

技术标签:

【中文标题】Django - 如何创建文件并将其保存到模型的 FileField?【英文标题】:Django - how to create a file and save it to a model's FileField? 【发布时间】:2011-11-22 19:38:44 【问题描述】:

这是我的模型。我想要做的是生成一个新文件并在保存模型实例时覆盖现有文件:

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 将其存储在正确的位置?

【问题讨论】:

【参考方案1】:

您想查看 Django 文档中的 FileField and FieldFile,尤其是 FieldFile.save()。

基本上,声明为FileField 的字段在访问时会为您提供FieldFile 类的实例,它为您提供了与底层文件交互的多种方法。所以,你需要做的是:

self.license_file.save(new_name, new_contents)

其中new_name 是您希望分配的文件名,new_contents 是文件的内容。请注意,new_contents 必须是 django.core.files.Filedjango.core.files.base.ContentFile 的一个实例(有关详细信息,请参阅手册提供的链接)。

这两种选择归结为:

from django.core.files.base import ContentFile, File

# Using File
with open('/path/to/file') as f:
    self.license_file.save(new_name, File(f))

# Using ContentFile
self.license_file.save(new_name, ContentFile('A string with the file content'))

【讨论】:

好的,我认为这会起作用,但我正在进入某种递归循环,在 save 方法中调用它。它只会永远创建文件。 对于递归问题,我必须使用参数 save=False 调用 self.license_file.save。 这个 (ContentFile) 与 django-wkhtmltopdf 的 convert_to_pdf 命令返回的文件字符串完美配合。谢谢!! 在我的情况下,上面没有将文件保存到文件夹中。原来问题是我正在使用docker-compose 来运行我的 django 应用程序和一个芹菜工人。 MEDIA_ROOT 的 django 应用程序卷未与 celery worker 中的同一卷共享。共享命名卷修复了它 (ref)。 @user5359531 from django.core.files import File【参考方案2】:

接受的答案当然是一个很好的解决方案,但这是我生成 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

【讨论】:

【参考方案3】:

在文件保存过程中出现异常时,最好使用上下文管理器或调用close()。如果您的存储后端出现故障等情况,可能会发生。

应在您的存储后端配置任何覆盖行为。例如,S3Boto3Storage 有一个设置AWS_S3_FILE_OVERWRITE。如果你使用FileSystemStorage,你可以写一个custom mixin。

如果您希望发生任何自定义副作用,例如上次更新的时间戳,您可能还想调用模型的保存方法而不是 FileField 的保存方法。如果是这种情况,您还可以将文件的 name 属性设置为文件的名称 - 相对于 MEDIA_ROOT。它默认为文件的完整路径,如果您不设置它可能会导致问题 - 请参阅 File.__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()

【讨论】:

以上是关于Django - 如何创建文件并将其保存到模型的 FileField?的主要内容,如果未能解决你的问题,请参考以下文章

Django 在 MEDIA_ROOT 文件夹中创建文件并将其保存到 FileField

如何在javascript中获取django表单值而不将其保存到模型中

从 GraphQL 计算一个值并将其保存在 Django 模型中

如何计算Django模型中某些字段的平均值并将其发送到rest API?

React Native:如何创建文本文件并将其保存到外部存储

如何使用 GeoIP2() django 模型从 IP 地址调用中提取保存的信息以显示在我的 html 中