我在磁盘上有一个现有文件(例如 /folder/file.txt),以及Django中的FileField模型字段。
当我执行以下操作时:
instance.field = File(file('/folder/file.txt'))
instance.save()
当再次保存文件时,它会将文件另存为file_1.txt
(下一次是_2
,以此类推)。
我理解原因,但我不想这样做 - 我知道我想让该字段关联的文件确实已经等待着我,我只想让Django指向它。
如何实现?
只需将instance.field.name
设置为您文件的路径即可。
例如:
class Document(models.Model):
file = FileField(upload_to=get_document_path)
description = CharField(max_length=100)
doc = Document()
doc.file.name = 'path/to/file' # must be relative to MEDIA_ROOT
doc.file
<FieldFile: path/to/file>
doc.file = '文件路径'
。 - Andrew Swihart如果您想永久地做到这一点,您需要创建自己的FileStorage类。
import os
from django.conf import settings
from django.core.files.storage import FileSystemStorage
class MyFileStorage(FileSystemStorage):
# This method is actually defined in Storage
def get_available_name(self, name):
if self.exists(name):
os.remove(os.path.join(settings.MEDIA_ROOT, name))
return name # simply returns the name passed
现在在你的模型中,你使用了修改过的MyFileStorage。
from mystuff.customs import MyFileStorage
mfs = MyFileStorage()
class SomeModel(model.Model):
my_file = model.FileField(storage=mfs)
FilePathField
或纯文本中来避免所有这些问题。 - Burhan Khalidget_available_name
方法是不正确的。get_available_name
方法。它不是导致重命名的方法。导致重命名的方法是 _save
。在 _save
中有很好的注释,您可以轻松找到它使用标志 os.O_EXCL
打开文件进行写入,如果相同的文件名已经存在,则会抛出 OSError。Django 捕获此错误,然后调用 get_available_name
获取新名称。_save
并调用 os.open() 而不使用标志 os.O_EXCL
。修改非常简单,但是该方法有点长,所以我不在这里粘贴它。如果您需要更多帮助,请告诉我 :)get_available_name
的问题在于,当您上传具有相同名称的文件时,服务器将陷入无限循环。由于_save
检查文件名并决定获取新文件名,但是get_available_name
仍然返回重复的文件名。因此,您需要同时覆盖两者。 - x1a0如果您使用应用程序的文件系统来存储文件,则答案可以正常工作。但是,如果您正在使用boto3并上载到诸如AWS S3之类的东西,并且可能希望将已存在于S3存储桶中的文件设置为模型的FileField,则需要执行以下操作。
我们有一个带有filefield的简单模型类:
class Image(models.Model):
img = models.FileField()
owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='images')
date_added = models.DateTimeField(editable=False)
date_modified = models.DateTimeField(editable=True)
from botocore.exceptions import ClientError
import boto3
s3 = boto3.client(
's3',
aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY")
)
s3_key = S3_DIR + '/' + filename
bucket_name = os.getenv("AWS_STORAGE_BUCKET_NAME")
try:
s3.upload_file(local_file_path, bucket_name, s3_key)
# we want to store it to our db model called **Image** after s3 upload is complete so,
image_data = Image()
image_data.img.name = s3_key # this does it !!
image_data.owner = get_user_model().objects.get(id=owner_id)
image_data.save()
except ClientError as e:
print(f"failed uploading to s3 {e}")
OS_OPEN_FLAGS
类属性和get_available_name()
方法:
Django版本:3.1
Project/core/files/storages/backends/local.py
import os
from django.core.files.storage import FileSystemStorage
class OverwriteStorage(FileSystemStorage):
"""
FileSystemStorage subclass that allows overwrite the already existing
files.
Be careful using this class, as user-uploaded files will overwrite
already existing files.
"""
# The combination that don't makes os.open() raise OSError if the
# file already exists before it's opened.
OS_OPEN_FLAGS = os.O_WRONLY | os.O_TRUNC | os.O_CREAT | getattr(os, 'O_BINARY', 0)
def get_available_name(self, name, max_length=None):
"""
This method will be called before starting the save process.
"""
return name
myapp/models.py
from django.db import models
from core.files.storages.backends.local import OverwriteStorage
class MyModel(models.Model):
my_file = models.FileField(storage=OverwriteStorage())
我曾经遇到过完全相同的问题!后来我意识到是我的模型引起了这个问题。例如,我的模型就像这样:
class Tile(models.Model):
image = models.ImageField()
接下来,我想让多个瓷砖引用磁盘上的同一个文件!我找到的解决方法是改变我的模型结构如下:
class Tile(models.Model):
image = models.ForeignKey(TileImage)
class TileImage(models.Model):
image = models.ImageField()
之后我意识到这更有意义,因为如果我想要同一文件在我的数据库中保存多次,我必须为其创建另一个表格!
我猜你也可以像这样解决问题,只是希望你能改变模型!
编辑
此外,我猜你也可以使用不同的存储方式,比如 SymlinkOrCopyStorage。
http://code.welldev.org/django-storages/src/11bef0c2a410/storages/backends/symlinkorcopy.py
FileField
的情况下获得所需内容。每当保存FileField
时,都会创建文件的新副本。添加一个选项以避免这种情况将是相当简单的。 - Michael Mior