Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - Cannot create migrations for ImageField with dynamic upload_to value

I just upgraded my app to 1.7 (actually still trying).

This is what i had in 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")

When i try to makemigrations, it throws:

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.
like image 467
alioguzhan Avatar asked Sep 10 '14 14:09

alioguzhan


3 Answers

I am not sure if it is OK to answer my own question, but i just figured out (i think).

According to this bug report, i edited my code:

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")

And then, in field definition:

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")

This worked for me.

like image 135
alioguzhan Avatar answered Nov 20 '22 14:11

alioguzhan


I had the same problem but I have many ImageFile in my models

head = ImageField(upload_to=upload_to("head")
icon = ImageField(upload_to=upload_to("icon")
...etc

I don't want create upload_to wraper func for every ImageField column I have.

So I just create a func named wrapper, and it woks

def wrapper():
    return

I think it works just fine because I opened migration file and I found this:

('head', models.ImageField(upload_to=wrapper)),

I guess it's not effect migration process

like image 22
Juntao Avatar answered Nov 20 '22 15:11

Juntao


You can create function with kwargs like this:

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

and use this method with functools.partial in your model:

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)

You will get migration like this:

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'),
        ),
    ]
like image 1
QuadX Avatar answered Nov 20 '22 14:11

QuadX