如何读取存储文件保存为model.FileField对象,解决I/O关闭报错?
问题原因分析
- 文件句柄提前关闭+打开模式错误:你使用
with上下文管理器打开文件,with块执行结束后会自动关闭文件句柄,但Django的File对象在模型执行save()操作时才会真正读取文件内容,此时文件已被关闭,直接触发I/O operation on closed file错误。同时你打开PDF二进制文件使用了文本模式'r',即使文件不关闭也会出现读取内容异常的问题,需要改为二进制读取模式'rb'。 - 信号逻辑错误,存在死循环风险:你在
MyModel的post_save信号处理函数中,又新建了MyModel实例并调用save(),这会再次触发post_save信号,陷入无限循环创建实例的问题,且你的需求是给当前刚创建的实例设置默认文件,完全不需要新建实例。 - 字段赋值逻辑错误:
FileField字段赋值后需要调用自身的save()方法处理文件存储逻辑,直接赋值给模型属性可能无法正确写入存储。
修正后的代码
from django.core.files import File from django.db.models.signals import post_save from django.dispatch import receiver @receiver(post_save, sender=MyModel) def post_save_mymodel_setup(sender, instance, created, **kwargs): # 仅在实例刚创建、且未上传文件时设置默认值 if created and not instance.myfile: # 用二进制模式打开默认文件,文件操作放在with块内,保证读取时文件未关闭 with open('/path/to/default_file.pdf', 'rb') as f: file_obj = File(f, name='default_file.pdf') # 调用FileField的save方法,save参数设为False避免重复触发模型save instance.myfile.save('default_file.pdf', file_obj, save=False) # 用update方法更新字段,不会触发post_save信号,避免死循环 MyModel.objects.filter(pk=instance.pk).update(myfile=instance.myfile)
额外优化建议
如果不需要依赖实例创建后的其他逻辑,也可以直接给FileField设置default参数指定默认文件,不需要额外写信号,更简洁:
def get_default_file(): from django.core.files import File f = open('/path/to/default_file.pdf', 'rb') return File(f, name='default_file.pdf') class MyModel(models.Model): myfile = models.FileField(default=get_default_file)
内容的提问来源于stack exchange,提问作者alias51
相关产品推荐
相关产品推荐

