Проблема с настройкой пути загрузки для ImageField и FileField в Django 2.2 - PullRequest
0 голосов
/ 19 апреля 2019

Я создал функцию вне модели, которая будет использоваться в качестве вспомогательной функции, которая будет работать с любым методом, просто передав ему строку 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 голосов
/ 19 апреля 2019

Это немного сложно. Команда Django makemigrations пытается сгенерировать файлы миграции программным способом.

Когда вы передаете функцию в ключевое слово upload_to или default аргумент поля модели, он импортирует весь модуль, содержащий эту функцию, в файл миграции. Так что в вашем случае 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)
...