【问题标题】:Do not save model on duplicate file - Django 2不要将模型保存在重复文件上 - Django 2
【发布时间】:2019-04-29 07:08:37
【问题描述】:

我正在尝试检查现有文件并覆盖它,到目前为止,我可以使用自定义存储来完成它,看起来像这样

from django.core.files.storage import FileSystemStorage

class DuplicateFileStorage(FileSystemStorage):

    def get_available_name(self, name, max_length=None):

        return name

    def _save(self, name, content):
        if self.exists(name):
            self.delete(name)

        return super(DuplicateFileStorage, self)._save(name, content)

上述类检查现有文件并将其删除。

它所关注的模型是:

class DataStorageModel(models.Model):

    user = models.ForeignKey(User, related_name='data_storage', on_delete=models.CASCADE)
    file_name = models.CharField(blank=True, max_length=200, help_text="File Name")
    file = models.FileField(blank=True, null=True, upload_to=user_directory_path, storage=DuplicateFileStorage())
    relative_path = models.CharField(blank=True, max_length=200, help_text="Relative Path if folder else null")

    def delete(self, using=None, keep_parents=False):

        self.file.delete()

        return super(DataStorageModel, self).delete()

问题在于,虽然它删除并写入了同一个文件,但它也会创建一个具有相同现有路径的新模型条目,也就是说,如果我两次上传同一个文件,我会在操作系统路径中得到一个文件,但有两个模型条目.像这样的东西(这里有一张图片):

所以我尝试将clean() 方法与self.file.storage.exists(self.file.name) 一起使用(根据this),但我得到False 的存在,即使那里有一个文件。

def save(self, force_insert=False, force_update=False, using=None,
         update_fields=None):

    self.full_clean()

    return super(DataStorageModel, self).save()

def clean(self):

    print(self.file.storage.exists(self.file.name))  # <--- False

所以我的问题是,我应该如何检查是否存在重复文件,覆盖文件但不创建新记录?

编辑

我忘了输入user_director_path():

def user_directory_path(instance, filename):
    """
    If relative path is not ``null`` the files will be stored as is else it will be
    stored to the root directory.
    """

    if instance.relative_path != 'null':
        return 'user_{0}{1}'.format(instance.user.id, instance.relative_path)
    return 'user_{0}/{1}'.format(instance.user.id, filename)

如果我这样做,我想我明白了:

def clean_fields(self, exclude=None):

    if self.file.storage.exists('user_{0}{1}'.format(self.user.id, self.relative_path)):
        raise ValidationError('File already exists.')

它可以工作,但整个上传失败,甚至没有重写文件

【问题讨论】:

  • 你试过update_or_create() ??]
  • 我应该在哪里使用它?在clean 或save() 中,因为如果我在另存为DataStorageModel.objects.update_or_create(user=self.user, file=self.file) 时使用它,我会得到RecursionError: maximum recursion depth exceeded while calling a Python object 如果我使用self.objects.update_or_create(user=self.user, file=self.file) 我无法联系到经理。
  • 你也应该提供defaults

标签: python django


【解决方案1】:

条件,if instance.relative_path != 'null': 是错误的。应该只是 if instance.relative_path:

当我尝试你的 sn-p 时,我觉得 user_directory_path 功能有问题。因此,我将其更改为如下所示。

def user_directory_path(instance, filename):
    """
    If relative path is not ``null`` the files will be stored as is else it will be
    stored to the root directory.

    The "relative_path" path should not be start or ends with a slash ("/") but, can use slash inside

    /foo/ -> Not allowed
    /foo/bar -> Not allowed
    foo/bar/ -> Not allowed

    foo -> Allowed
    foo/bar -> Allowed
    foo/bar/foobar -> Allowed

    """

    if instance.relative_path:
        relative_path = instance.relative_path
        if relative_path[0] == '/':
            relative_path = relative_path[1:]
        if relative_path[:-1] == '/':
            relative_path = relative_path[:-1]

        return 'user_{0}/{1}/{2}'.format(instance.user.id, relative_path, filename)
    return 'user_{0}/{1}'.format(instance.user.id, filename)

现在来到我们的解决方案,我创建了一个示例视图来解决这个问题。

from django.http.response import HttpResponse


def foo(request):
    if request.method == 'POST':
        create_data = {
            "user_id": request.user.pk,
            "file": request.FILES['file_name']
        }

        ds_temp = DataStorageModel(**create_data)
        path = user_directory_path(ds_temp, ds_temp.file.name)
        try:
            ds_existing = DataStorageModel.objects.get(file=path)
            ds_existing.file = create_data['file']
            ds_existing.save()
            return HttpResponse("instance modified")
        except DataStorageModel.DoesNotExist:
            ds_temp.save()
            return HttpResponse("new instance created")
    return HttpResponse("HTTP GET method")

【讨论】:

  • 没错,我忘了这应该在视图而不是模型中完成。 user_directory_path 你写的内容为每个文件创建一个文件夹,即 - temp.txt 它创建 temp.txt/temp.txt
  • 我的逻辑是,如果存在相对路径,就会在用户目录下创建子目录。如果相对路径不存在,文件将被保存到用户目录本身
  • 是的,如果我上传一个文件夹,我会得到 something/something-text.txt 的相对路径和单独的文件名,(我正在使用 uppy.js)
  • 正在上传文件夹? (文件夹还是文件?)
  • 实际上,如果我上传一个文件夹,我只会得到该文件夹​​的相对路径,否则我会得到相对路径为空。
猜你喜欢
  • 2023-02-13
  • 2015-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-12
  • 1970-01-01
  • 2021-08-17
相关资源
最近更新 更多