Django中如何将一个FileField的文件复制到另一个不同上传路径的FileField?
解决Django FileField自动备份文件的问题
我来帮你搞定这个文件备份的问题!你当前的代码只是把attachment_number的文件路径赋值给了attachment_copy,但并没有实际将文件内容复制到备份存储的目录下——Django的FileField不会自动帮你复制文件,得手动处理文件的读写操作才行。
步骤1:优化模型的存储路径(可选但建议)
你现在的UPLOAD_ROOT和UPLOAD_COPY都是C:/,这会导致源文件和备份文件混在同一个根目录,容易混乱。建议改成不同的独立路径:
class MyModal(models.Model): UPLOAD_ROOT = "C:/uploads/" UPLOAD_COPY = "C:/uploads_backup/" upload_storage = FileSystemStorage(location=UPLOAD_ROOT, base_url="/uploads") upload_storage_copy = FileSystemStorage(location=UPLOAD_COPY, base_url="/uploads_backup") # 其他字段保持不变...
另外,你的file_path_dir_copy函数生成的路径有点冗余(会出现FileBackUp/FileBackUp2024.05.20/xxx这种嵌套),可以简化一下:
def file_path_dir_copy(instance, filename): # 只保留日期作为子目录,路径更简洁 return "FileBackUp/{0}/{1}".format(datetime.now().strftime("%Y.%m.%d"), filename)
步骤2:修改save_model方法,实现真实文件复制
核心逻辑是读取源文件的内容,然后写入到备份存储系统中。替换你当前的save_model方法:
import os from django.core.files.base import ContentFile def save_model(self, request, obj, *args, **kwargs): # 先检查源文件是否存在,避免空文件报错 if obj.attachment_number and obj.attachment_number.file: # 获取源文件的纯文件名 filename = os.path.basename(obj.attachment_number.name) # 调用upload_to函数生成备份文件的目标路径 backup_path = file_path_dir_copy(obj, filename) # 读取源文件的二进制内容 with obj.attachment_number.open('rb') as source_file: file_content = source_file.read() # 将内容写入备份字段,save=False避免提前触发模型保存 obj.attachment_copy.save(backup_path, ContentFile(file_content), save=False) # 调用父类方法完成最终的模型保存 super().save_model(request, obj, *args, **kwargs)
代码说明:
- 先判断
attachment_number是否有有效文件,防止空指针异常。 - 使用
with语句读取源文件,确保文件资源能自动释放。 - 通过
obj.attachment_copy.save()主动将文件内容写入备份存储,这一步才是真正完成文件复制的关键。
额外适配:处理多实例场景(如果用了FormSet)
如果你的obj是FormSet返回的多个实例集合,需要调整循环逻辑确保每个对象都被处理:
def save_model(self, request, obj, *args, **kwargs): # 判断是否为多实例集合 if hasattr(obj, '__iter__'): for sub_obj in obj: if sub_obj.attachment_number and sub_obj.attachment_number.file: filename = os.path.basename(sub_obj.attachment_number.name) backup_path = file_path_dir_copy(sub_obj, filename) with sub_obj.attachment_number.open('rb') as source_file: file_content = source_file.read() sub_obj.attachment_copy.save(backup_path, ContentFile(file_content), save=False) else: # 单个实例的处理逻辑 if obj.attachment_number and obj.attachment_number.file: filename = os.path.basename(obj.attachment_number.name) backup_path = file_path_dir_copy(obj, filename) with obj.attachment_number.open('rb') as source_file: file_content = source_file.read() obj.attachment_copy.save(backup_path, ContentFile(file_content), save=False) super().save_model(request, obj, *args, **kwargs)
最后要注意:确保你的存储目录(C:/uploads/和C:/uploads_backup/)已经存在,并且Django进程有读写权限,否则会抛出文件操作错误。
内容的提问来源于stack exchange,提问作者Dhia Shalabi
相关产品推荐
相关产品推荐

