在Django 2.2中为ImageField和FileField自定义上传路径的问题

时间:2019-04-19 15:54:02

标签: django python-3.x django-models python-decorators higher-order-functions

我在模型之外创建了一个函数,用作实用程序函数,该函数可以通过向其传递一个path字符串来与任何方法一起使用,并且它将返回文件路径以及重命名的文件名。更改文件名的function(instance, filename)被包装在一个接受path字符串的包装函数中。

以下是函数(存储在另一个应用程序的helpers.py中):

def path_and_rename(path):
    """
    Returns wrapper func
    :param path: path string with slash at the end
    :return: func
    """
    def wrapper(instance, filename):
        """
        Returns a filename string, both
        and filename, with filename as an md5 string
        :param instance: model instance with the file_field or image_field
        :param filename: filename as uploaded (as received from the model)
        :return: str
        """
        ext = filename.split('.')[-1]  # Preserve image file extension
        md5_file_name = f"{hashlib.md5(str(filename).encode('utf-8')).hexdigest()}.{ext}"  # md5 from filename
        return f"{path}{md5_file_name}"
    return wrapper

在我的模型中,我执行了以下操作:

image = ImageField(verbose_name=_("Product image"), upload_to=path_and_rename("products/images/"))

但这会在makemigrations上产生错误:

'Could not find function %s in %s.\n' % (self.value.__name__, module_name)
ValueError: Could not find function wrapper in my_app_root.core.helpers.

1 个答案:

答案 0 :(得分:0)

有点棘手。 Django makemigrations命令尝试以编程方式生成迁移文件。

将函数传递给模型字段的upload_todefault关键字参数时,它将在该文件中导入包含该函数的整个模块。因此,在您的情况下,Django将在将要生成的迁移文件之上编写以下导入。

import my_app_root.core.helpers

此后,它将尝试通过__qualname__ 从导入的模块中获取该功能的引用。因为在您的情况下,最终将用于获取路径的函数是另一个函数返回的wrapper,因此django会尝试做my_app_root.core.helpers.wrapper,这肯定会(现在)失败了。

因此,最终的解决方案是使用模块级函数作为upload_to参数的引用。但是,可能有些棘手(可能很丑陋)的解决方案是将函数调用分配给变量,然后为其分配一个__qualname__,其名称与此类似。

def path_and_rename(path):
    # all the functionality here


product_image_upload_path = path_and_rename('products/images/')
# assign it `__qualname__`
product_image_upload_path.__qualname__ = 'product_image_upload_path'

然后像这样在模型字段中使用此变量。

image = ImageField(upload_to=product_image_upload_path)