Opened 7 years ago

Closed 7 years ago

#22999 closed Bug (invalid)

Impossible to generate migration for model with dynamic upload_to field

Reported by: Kevin Van Wilder <kevin@…> Owned by: nobody
Component: Migrations Version: 1.7-rc-1
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

In my application I have a model which uploads forms to a specific user directory for better file system organization:

media_root/<username>/reports/file

To accomplish this, I use the following model:

upload_dir = upload_to_student_dir('committee/reports/')


class CommitteeReport(TimeStampedModel):
    user = models.ForeignKey(User)
    document = models.FileField(upload_to=upload_dir)

The function defines and returns an inner function which will provide the complete path based on the instance's user:

def upload_to_student_dir(sub_path=''):
    def callable(instance, filename):
        """Dynamically returns the user directory to which this file should be uploaded.
        """
        path = "students/{0}/{1}{2}".format(instance.user.username, sub_path, filename)
        return path
    return callable

Creating a migration via South seemed to have caused no problems, yet the analysis being employed by django.db.migrations differs and generates the following error:

Migrations for 'committee':
  0001_initial.py:
    - Create model CommitteeReport
Traceback (most recent call last):
  File "manage.py", line 11, in <module>
    execute_from_command_line(sys.argv)
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 377, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/core/management/base.py", line 337, in execute
    output = self.handle(*args, **options)
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/core/management/commands/makemigrations.py", line 115, in handle
    self.write_migration_files(changes)
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/core/management/commands/makemigrations.py", line 143, in write_migration_files
    migration_string = writer.as_string()
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/db/migrations/writer.py", line 124, in as_string
    operation_string, operation_imports = OperationWriter(operation).serialize()
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/db/migrations/writer.py", line 76, in serialize
    arg_string, arg_imports = MigrationWriter.serialize(item)
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/db/migrations/writer.py", line 229, in serialize
    item_string, item_imports = cls.serialize(item)
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/db/migrations/writer.py", line 290, in serialize
    return cls.serialize_deconstructed(path, args, kwargs)
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/db/migrations/writer.py", line 205, in serialize_deconstructed
    arg_string, arg_imports = cls.serialize(arg)
  File "/home/kevin/.virtualenvs/stageportfolio/local/lib/python2.7/site-packages/django/db/migrations/writer.py", line 316, in serialize
    "Could not find function %s in %s.\nPlease note that "
ValueError: Could not find function %s in %s.
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.7/topics/migrations/#serializing-values

As you can see I have already moved the function into the main module, yet the error persists.

Change History (1)

comment:1 Changed 7 years ago by Simon Charette

Resolution: invalid
Status: newclosed

The function defines and returns an inner function which will provide the complete path based on the instance's user...

As you can see I have already moved the function into the main module, yet the error persists.

What you've done is moving the function factory to the main module, as described by the exception message there's no way Django can reconstruct the returned inner function.

I suggest you define a callable factory class that implements deconstruct instead:

from django.utils.deconstruct import deconstructible

@deconstructible
class UploadToStudentDir(object):
    path = "students/{0}/{1}{2}"

    def __init__(self, sub_path):
        self.sub_path = sub_path

    def __call__(self, instance, filename):
        return path.format(instance.user.username, self.sub_path, filename)

upload_dir = UploadToStudentDir('committee/reports/')

This use to work with South because it didn't serialize the upload_to option of the FileField and it subclasses.

I'll mark as invalid since this is a well known limitation of the migration framework.

Note: See TracTickets for help on using tickets.
Back to Top