Django: no se pueden crear migraciones para ImageField con el valor dynamic upload_to

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 bash 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 (eg 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í.

    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 simplemente 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.

    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á una migración como esta:

     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'), ), ]