【问题标题】:Problem with customising upload path for ImageField and FileField in Django 2.2在 Django 2.2 中自定义 ImageField 和 FileField 的上传路径的问题
【发布时间】:2019-09-09 21:08:57
【问题描述】:

我在模型之外创建了一个函数,用作实用函数,只需将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.

【问题讨论】:

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


    【解决方案1】:

    这有点棘手。 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)
    

    【讨论】:

    • 当你说“所以最终的解决方案是使用模块级函数作为upload_to参数的参考”。你到底是什么意思?
    • 模块级别意味着可以从模块中导入的东西。例如,在您的情况下,path_and_rename 是模块级函数,但 wrapper 函数不是。我发布的解决方案是欺骗 django 认为 product_image_upload_path 是一个模块级函数。
    • 那么我该如何在模型字段中实现呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-08
    • 2016-05-24
    • 2013-12-24
    • 2016-02-10
    • 1970-01-01
    • 2012-05-08
    • 2010-12-04
    相关资源
    最近更新 更多