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

#django #python-3.x #django-models #python-decorators #higher-order-functions

#django #python-3.x #django-модели #python-декораторы #функции более высокого порядка

Вопрос:

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

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

Когда вы передаете функцию upload_to или default аргумент ключевого слова поля модели, он импортирует весь модуль, содержащий эту функцию, в файл миграции. Итак, в вашем случае Django запишет следующий импорт поверх файла миграции, который он собирается сгенерировать.

 import my_app_root.core.helpers
 

После этого он попытается получить ссылку на эту функцию __qualname__ из импортированного модуля. Поскольку в вашем случае функция, которая в конечном итоге будет использоваться для получения path wrapper , возвращается другой функцией, django попытается выполнить my_app_root.core.helpers.wrapper , что будет (и есть) определенно неудачным.

Таким образом, конечным решением является использование функции уровня модуля в качестве ссылки для upload_to аргумента. Однако одним немного сложным (и может быть уродливым) решением может быть назначение вызова функции переменной и присвоение ей a __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)
 

Комментарии:

1. Когда вы сказали: » Итак, конечным решением является использование функции уровня модуля в качестве ссылки для upload_to аргумента «. Что именно вы имели в виду?

2. Уровень модуля означает то, что можно импортировать из модуля. Например, в вашем случае path_and_rename это функция уровня модуля wrapper , а функция — нет. Решение, которое я опубликовал, заставляет django думать product_image_upload_path , что это функция уровня модуля.

3. Итак, как мне реализовать это в полях модели?