将Django的FileField设置为现有文件

时间:2011-11-30 20:20:29

标签: python django file

我在磁盘上有一个现有文件(例如/folder/file.txt)和Django中的FileField模型字段。

当我这样做时

instance.field = File(file('/folder/file.txt'))
instance.save()

它将文件重新保存为file_1.txt(下次是_2等)。

我理解为什么,但我不想要这种行为 - 我知道我希望该字段与之关联的文件真的在那里等着我,我只想让Django指向它。

如何?

7 个答案:

答案 0 :(得分:98)

只需将instance.field.name设置为文件的路径

即可

e.g。

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>

答案 1 :(得分:18)

如果您想永久执行此操作,则需要创建自己的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)

答案 2 :(得分:9)

试试这个(doc):

instance.field.name = <PATH RELATIVE TO MEDIA_ROOT> 
instance.save()

答案 3 :(得分:4)

编写自己的存储类是正确的。但是get_available_name不是覆盖的正确方法。

当Django看到具有相同名称的文件并尝试获取新的可用文件名时,将调用

get_available_name。这不是导致重命名的方法。导致的方法是_save_save中的注释相当不错,您可以轻松找到它打开文件,使用标记os.O_EXCL进行写入,如果已存在相同的文件名,则会抛出OSError。 Django捕获此错误,然后调用get_available_name获取新名称。

所以我认为正确的方法是覆盖_save并调用不带标记os.O_EXCL的os.open()。修改很简单但是方法有点长,所以我不在这里粘贴它。如果您需要更多帮助,请告诉我:)

答案 4 :(得分:1)

您应该定义自己的存储,从FileSystemStorage继承它,并覆盖OS_OPEN_FLAGS类属性和get_available_name()方法:

Django版本: 3.1

项目/核心/文件/存储/后端/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

在模型中,使用自定义的OverwriteStorage

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

答案 5 :(得分:0)

我有完全相同的问题!然后我意识到我的模型造成了这种情况。例如,我喜欢这样的模型:

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

答案 6 :(得分:0)

如果您使用应用程序的文件系统来存储您的文件,这些答案就可以正常工作。但是,如果您使用 boto3 并上传到诸如 AWS S3 之类的东西,并且您可能想将一个存在于 S3 存储桶中 的文件设置为模型的 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}")

S3 KEY 设置到 FileFieldname 字段中即可。我已经按预期测试了所有相关的工作,例如在 django admin 中预览图像文件。从 db 获取图像也会将根 s3 存储桶前缀(或 cloudfront cdn 前缀)附加到文件的 s3 键。当然,鉴于此,我已经为 boto 和 s3 设置了 django settings.py。