python - migrations - Django: no se pueden crear migraciones para ImageField con el valor dinámico upload_to
reset all migrations (3)
Acabo de actualizar mi aplicación a 1.7 (en realidad aún estoy intentando).
Esto es lo que tenía en models.py:
def path_and_rename(path):
def wrapper(instance, filename):
ext = filename.split(''.'')[-1]
# set filename as random string
filename = ''{}.{}''.format(uuid4().hex, ext)
# return the whole path to the file
return os.path.join(path, filename)
return wrapper
class UserProfile(AbstractUser):
#...
avatar = models.ImageField(upload_to=path_and_rename("avatars/"),
null=True, blank=True,
default="avatars/none/default.png",
height_field="image_height",
width_field="image_width")
Cuando intento hacer makemigrations
, arroja:
ValueError: Could not find function wrapper in webapp.models.
Please note that due to Python 2 limitations, you cannot serialize unbound method functions (e.g. a method declared
and used in the same class body). Please move the function into the main module body to use migrations.
No estoy seguro de si está bien responder mi propia pregunta, pero acabo de descubrir (creo).
Según este informe de error , edité mi código:
from django.utils.deconstruct import deconstructible
@deconstructible
class PathAndRename(object):
def __init__(self, sub_path):
self.path = sub_path
def __call__(self, instance, filename):
ext = filename.split(''.'')[-1]
# set filename as random string
filename = ''{}.{}''.format(uuid4().hex, ext)
# return the whole path to the file
return os.path.join(self.path, filename)
path_and_rename = PathAndRename("/avatars")
Y luego, en definición de campo:
avatar = models.ImageField(upload_to=path_and_rename,
null=True, blank=True,
default="avatars/none/default.png",
height_field="image_height",
width_field="image_width")
Esto funcionó para mí.
Puedes crear una función con kwargs así:
def upload_image_location(instance, filename, thumbnail=False):
name, ext = os.path.splitext(filename)
path = f''news/{instance.slug}{f"_thumbnail" if thumbnail else ""}{ext}''
n = 1
while os.path.exists(path):
path = f''news/{instance.slug}-{n}{ext}''
n += 1
return path
y use este método con functools.partial en su modelo:
image = models.ImageField(
upload_to=upload_image_location,
width_field=''image_width'',
height_field=''image_height''
)
thumbnail_image = models.ImageField(upload_to=partial(upload_image_location, thumbnail=True), blank=True)
Obtendrá la migración de esta manera:
class Migration(migrations.Migration):
dependencies = [
(''news'', ''0001_initial''),
]
operations = [
migrations.AddField(
model_name=''news'',
name=''thumbnail_image'',
field=models.ImageField(blank=True, upload_to=functools.partial(news.models.upload_image_location, *(), **{''thumbnail'': True})),
),
migrations.AlterField(
model_name=''news'',
name=''image'',
field=models.ImageField(height_field=''image_height'', upload_to=news.models.upload_image_location, width_field=''image_width''),
),
]
Tuve el mismo problema pero tengo muchos ImageFile en mis modelos
head = ImageField(upload_to=upload_to("head")
icon = ImageField(upload_to=upload_to("icon")
...etc
No quiero crear la función upload_to wraper para cada columna ImageField que tengo.
Así que solo creo una función llamada wrapper, y funciona
def wrapper():
return
Creo que funciona bien porque abrí el archivo de migración y encontré esto:
(''head'', models.ImageField(upload_to=wrapper)),
Supongo que no es efecto del proceso de migración.