Samir Tendulkar
Samir Tendulkar

Reputation: 1191

ValueError: Please move the function into the main module body to use migrations

The error that I am trying to Fix

ValueError: Could not find function func in posts.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. For more information, see https: //docs.djangoproject.com/en/1.11/topics/migrations/#serializing-values

Intro: I am using Django==1.11.20 and Ubuntu 18.04. My python version is 3.6.7 When I do in my Django project in the

(venv)some_path$ python --version
Python 3.6.7

However when I do the same in my Ubuntu terminal I get

marco@VB:~$ python --version
Python 2.7.15rc1

marco@VB:~$ python3 --version
Python 3.6.7

About my project: I have the below models

def upload_name (user_fld='user', prefix=None):
    def func(instance, fname):
        #return os.path.join(prefix, fname) if prefix else fname
        attrs = user_fld.split('.')
        user = instance
        print ("Getattr %s %s" % (attrs, user))
        try:
            for attr in attrs:
                user = getattr(user, attr)
        except:
            username = 'anon'
        else:
            username = user.username

        print ("Upload name** %s, %s" % (instance.pk, username))
        # Extract the raw fname
        fparts = fname.split('.')
        base = fparts[0]
        try:
            atoms = base.split("_")
            ts = datetime.strptime('_'.join(atoms[-7:]), "%Y_%m_%d_%H_%M_%S_%f")
            ousername = atoms[-8]
            base = '_'.join(atoms[:-8])
        except:
            # Any exception in handling this means it wasn't already equipped with our
            # extension so add it on
            pass

        ts = datetime.now()
        fname = '%s_%s_%s%s' % (base, username, ts.strftime("%Y_%m_%d_%H_%M_%S_%f"),
                                ('.%s' % '.'.join(fparts[1:])) if len(fparts) > 1 else '')
        return os.path.join(prefix, fname) if prefix else fname

    return func

class Post(models.Model):
    user = models.ForeignKey(User, related_name='posts')
    title = models.CharField(max_length=250, unique=True)
    slug = models.SlugField(allow_unicode=True, unique=True, max_length=500)
    post_image = models.ImageField(null=True, blank=True, upload_to=upload_name())

class Prep (models.Model): #(Images)
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='post_prep')
    image = models.ImageField(upload_to=upload_name('post.user', 'images/'), blank=True, null=True, default='')
    image_title = models.CharField(max_length=100, default='')

Trying to figure out how to add the deconstruct() method

https://docs.djangoproject.com/en/1.11/topics/migrations/#adding-a-deconstruct-method

enter image description here

Trying @RaydelMiranda solution

def upload_name_factory(instance, fname, user_fld='user', prefix=None):
    attrs = user_fld.split('.')
    user = instance
    print("Getattr %s %s" % (attrs, user))
    try:
        for attr in attrs:
            user = getattr(user, attr)
    except:
        username = 'anon'
    else:
        username = user.username

    print("Upload name** %s, %s" % (instance.pk, username))
    # Extract the raw fname
    fparts = fname.split('.')
    base = fparts[0]
    try:
        atoms = base.split("_")
        ts = datetime.strptime('_'.join(atoms[-7:]), "%Y_%m_%d_%H_%M_%S_%f")
        ousername = atoms[-8]
        base = '_'.join(atoms[:-8])
    except:
        # Any exception in handling this means it wasn't already equipped with our
        # extension so add it on
        pass

    ts = datetime.now()
    fname = '%s_%s_%s%s' % (base, username, ts.strftime("%Y_%m_%d_%H_%M_%S_%f"),
                            ('.%s' % '.'.join(fparts[1:])) if len(fparts) > 1 else '')
    return os.path.join(prefix, fname) if prefix else fname


upload_name = partial(upload_name_factory, user_fld='user', prefix=None)

Upvotes: 0

Views: 149

Answers (1)

Raydel Miranda
Raydel Miranda

Reputation: 14360

I see what you want to do, you want to be able to reuse the function you pass to upload_to being able to customize the user_fld and the prefix params.

So, you already know you have to move the function to the main module body. How to do that and keep your idea of reusability?

Partial functions.

from django.db import models
from functools import partial

def upload_name_factory(instance, filename, user_fld='user', prefix=None):
    attrs = user_fld.split('.')
    user = instance
    # ...

upload_name = partial(upload_name_factory, user_fld='user', prefix="/some/prefix/")

class Foo(models.Model):
    f = models.FileField(upload_to=upload_name)  

Read more about partials here.

Upvotes: 1

Related Questions