44

保存模型“产品”后,我希望上传的图像与 pk 命名相同,例如 22.png 或 34.gif 我不想仅更改名称的图像格式。如何才能做到这一点?到目前为止,我的模型示例...

image = models.ImageField(
        upload_to="profiles",
        height_field="image_height",
        width_field="image_width",
        null=True,
        blank=True,
        editable=True,
        help_text="Profile Picture",
        verbose_name="Profile Picture"
    )
    image_height = models.PositiveIntegerField(null=True, blank=True, editable=False, default="100")
    image_width = models.PositiveIntegerField(null=True, blank=True, editable=False, default="100")
4

6 回答 6

93

您可以将函数传递给upload_to字段:

def f(instance, filename):
    ext = filename.split('.')[-1]
    if instance.pk:
        return '{}.{}'.format(instance.pk, ext)
    else:
        pass
        # do something if pk is not there yet

我的建议是返回一个随机文件名而不是{pk}.{ext}. 作为奖励,它将更加安全。

发生的事情是 Django 将调用这个函数来确定文件应该上传到哪里。这意味着您的函数负责返回文件的整个路径,包括文件名。以下是修改后的函数,您可以在其中指定上传到哪里以及如何使用它:

import os
from uuid import uuid4

def path_and_rename(path):
    def wrapper(instance, filename):
        ext = filename.split('.')[-1]
        # get filename
        if instance.pk:
            filename = '{}.{}'.format(instance.pk, ext)
        else:
            # set filename as random string
            filename = '{}.{}'.format(uuid4().hex, ext)
        # return the whole path to the file
        return os.path.join(path, filename)
    return wrapper

FileField(upload_to=path_and_rename('upload/here/'), ...)
于 2013-02-28T16:56:05.820 回答
30

Django 1.7 和更新版本不会使用这样的功能进行迁移。根据@miki725 的回答和这张票,您需要使您的功能像这样:

import os
from uuid import uuid4
from django.utils.deconstruct import deconstructible

@deconstructible
class UploadToPathAndRename(object):

    def __init__(self, path):
        self.sub_path = path

    def __call__(self, instance, filename):
        ext = filename.split('.')[-1]
        # get filename
        if instance.pk:
            filename = '{}.{}'.format(instance.pk, ext)
        else:
            # set filename as random string
            filename = '{}.{}'.format(uuid4().hex, ext)
        # return the whole path to the file
        return os.path.join(self.sub_path, filename)

FileField(upload_to=UploadToPathAndRename(os.path.join(MEDIA_ROOT, 'upload', 'here'), ...)
于 2015-12-01T14:22:16.220 回答
7

您可以按照文档upload_to中的说明将分配给的字符串替换为可调用对象。但是,我怀疑在使用参数时主键可能不可用。upload_to

于 2013-02-28T16:53:14.040 回答
2

默认情况下,Django 保留上传文件的原始名称,但您很可能希望将其重命名为其他名称(例如对象的 id)。幸运的是,使用 Django 表单的 ImageField 或 FileField,您可以为 upload_to 参数分配一个可调用函数来进行重命名。例如:

from django.db import models
from django.utils import timezone
import os
from uuid import uuid4

def path_and_rename(instance, filename):
    upload_to = 'photos'
    ext = filename.split('.')[-1]
    # get filename
    if instance.pk:
        filename = '{}.{}'.format(instance.pk, ext)
    else:
        # set filename as random string
        filename = '{}.{}'.format(uuid4().hex, ext)
    # return the whole path to the file
    return os.path.join(upload_to, filename)

在模型领域:

class CardInfo(models.Model):
    ...
    photo = models.ImageField(upload_to=path_and_rename, max_length=255, null=True, blank=True)

在此示例中,上传的每张图片都将重命名为 CardInfo 对象的主键,即 id_number。

于 2020-11-01T14:43:10.470 回答
1

另一种选择,按照这个答案https://stackoverflow.com/a/15141228/3445802,当我们需要返回路径时,我们发现了问题%Y/%m/%d,例如:

FileField(upload_to=path_and_rename('upload/here/%Y/%m/%d'), ...)

所以,我们用这个来处理它:

FileField(upload_to=path_and_rename('upload/here/{}'.format(time.strftime("%Y/%m/%d"))), ...)

确保模块time已导入。

于 2016-04-26T13:18:45.113 回答
0

我确实有 Aidan Ewen 解决方案的更可定制的实现。

什么是新的?

  • 您可以将要在文件名中使用的字段作为列表发送(如预购)
  • ^ 其中一个必须是唯一的
  • ^ 否则,此列表必须包括(至少一个)唯一一起字段的元组
  • ^ 否则,您发送的字段将被忽略,并将使用 uuid4 作为文件名

示例 1:

image = models.ImageField(upload_to=PathAndRename('images/').wrapper)

filename = {pk}.{ext}
# default is pk for filenames

示例 2:

name = models.CharField(max_length=20)  # not unique
image = models.ImageField(upload_to=PathAndRename('images/', ['name']).wrapper)

filename = {uuid4}.{ext}
# if given fields are did not accepted will use the uuid4

示例 3:

name = models.CharField(max_length=20, unique=True)
no = models.CharField(max_length=10)
image = models.ImageField(upload_to=PathAndRename('images/', ['name','no']).wrapper)

filename = {name}_{no}.{ext}
# one unique field is enough to use all of the given fields in the filename

示例 4:

name = models.CharField(max_length=20)  # not unique
no = models.CharField(max_length=10)  # not unique
image = models.ImageField(upload_to=PathAndRename('images/', ['name','no']).wrapper)

class Meta:
    unique_together = ('name', 'no')
    # (('name', 'no'),) is acceptable too or multiple unique togethers

filename = {name}_{no}.{ext}
# if one of the unique together fields exists in the given fields, will use all of the given fields in the filename

我可能忘了再举一些例子,但你可以从下面的代码中理解:

class PathAndRename:
    """
    fields to use for naming, order is important
    """

    def __init__(self, path, fields_to_use=('pk',)):
        self.path = path
        self.fields_to_use = fields_to_use

    def wrapper(self, instance, filename):
        # multiple extensions
        ext = '.'.join(filename.split('.')[1:])

        # check the uniqueness of the fields given for filename
        if self.is_any_unique_exist(instance):
            # if any unique field exist in the given list
            # create filename by using given field values
            filename = '{}.{}'.format(self.get_filename_by_fields(instance), ext)
        # else check the existence of at least one unique together
        elif self.is_any_unique_together_exist(instance):
            # create filename by using given field values
            filename = '{}.{}'.format(self.get_filename_by_fields(instance), ext)
        # if any unique or unique together not exists
        else:
            # then create a filename by using uuid4
            filename = '{}.{}'.format(uuid4().hex, ext)

        # return the whole path to the file
        return os.path.join(self.path, filename)

    def is_any_unique_exist(self, instance):
        if 'pk' in self.fields_to_use:
            return True
        return any([instance._meta.get_field(field).unique for field in self.fields_to_use if hasattr(instance, field)])

    def is_any_unique_together_exist(self, instance):
        if hasattr(instance._meta, 'unique_together'):
            if isinstance(instance._meta.unique_together, (list, tuple)):
                for uniques in instance._meta.unique_together:
                    # if any one of the unique together set is exists in the fields to use
                    if all(map(lambda field: field in self.fields_to_use, uniques)):
                        return True
            else:
                if all(map(lambda field: field in self.fields_to_use, instance._meta.unique_together)):
                    return True
        return False

    def get_filename_by_fields(self, instance):
        return '_'.join([str(getattr(instance, field)) for field in self.fields_to_use])

警告:当您放弃使用这些解决方案时,针对此 upload_to 问题的每个基于方法的解决方案都会对执行的迁移文件造成问题。如果您使用这些解决方案一段时间然后删除它们,旧的迁移将由于这些方法不存在而失败。(当然,你可以通过修改旧的迁移文件来解决这个问题)

于 2021-08-22T18:56:23.503 回答