#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. Итак, как мне реализовать это в полях модели?