Repository: django-cms/django-filer

Issue:
Fix error in migrations caused by FilerFileField.
FilerFileField (and friends) were passing direct references to models in
for the to parameter of ForeignKey. This worked previously with south
migrations, but seems not to work very well with migrations in Django
1.8.

Please see https://github.com/fusionbox/filer-migration-bug-example for
more information.



Discussion:

Resolving patch:
diff --git a/filer/fields/file.py b/filer/fields/file.py
--- a/filer/fields/file.py
+++ b/filer/fields/file.py
@@ -10,6 +10,7 @@
 from django.utils.safestring import mark_safe
 
 from filer.utils.compatibility import truncate_words
+from filer.utils.model_label import get_model_label
 from filer.models import File
 from filer import settings as filer_settings
 
@@ -113,18 +114,15 @@ class FilerFileField(models.ForeignKey):
 
     def __init__(self, **kwargs):
         # We hard-code the `to` argument for ForeignKey.__init__
+        dfl = get_model_label(self.default_model_class)
         if "to" in kwargs.keys():  # pragma: no cover
-            old_to = kwargs.pop("to")
-            dfl = "%s.%s" % (
-                    self.default_model_class._meta.app_label,
-                    self.default_model_class.__name__
-            )
+            old_to = get_model_label(kwargs.pop("to"))
             if old_to != dfl:
                 msg = "%s can only be a ForeignKey to %s; %s passed" % (
-                    self.__class__.__name__, self.default_model_class.__name__, old_to
+                    self.__class__.__name__, dfl, old_to
                 )
                 warnings.warn(msg, SyntaxWarning)
-        kwargs['to'] = self.default_model_class
+        kwargs['to'] = dfl
         super(FilerFileField, self).__init__(**kwargs)
 
     def formfield(self, **kwargs):
diff --git a/filer/fields/folder.py b/filer/fields/folder.py
--- a/filer/fields/folder.py
+++ b/filer/fields/folder.py
@@ -10,6 +10,7 @@
 from django.db import models
 from django.utils.safestring import mark_safe
 from filer.utils.compatibility import truncate_words
+from filer.utils.model_label import get_model_label
 from django.utils.translation import ugettext as _
 from filer.models import Folder
 from filer.settings import FILER_STATICMEDIA_PREFIX
@@ -108,18 +109,15 @@ class FilerFolderField(models.ForeignKey):
 
     def __init__(self, **kwargs):
         # We hard-code the `to` argument for ForeignKey.__init__
+        dfl = get_model_label(self.default_model_class)
         if "to" in kwargs.keys():  # pragma: no cover
-            old_to = kwargs.pop("to")
-            dfl = "%s.%s" % (
-                    self.default_model_class._meta.app_label,
-                    self.default_model_class.__name__
-            )
+            old_to = get_model_label(kwargs.pop("to"))
             if old_to != dfl:
                 msg = "%s can only be a ForeignKey to %s; %s passed" % (
-                    self.__class__.__name__, self.default_model_class.__name__, old_to
+                    self.__class__.__name__, dfl, old_to
                 )
                 warnings.warn(msg, SyntaxWarning)
-        kwargs['to'] = self.default_model_class
+        kwargs['to'] = dfl
         super(FilerFolderField, self).__init__(**kwargs)
 
     def formfield(self, **kwargs):
diff --git a/filer/utils/model_label.py b/filer/utils/model_label.py
new file mode 100644
--- /dev/null
+++ b/filer/utils/model_label.py
@@ -0,0 +1,20 @@
+from django.utils import six
+
+
+def get_model_label(model):
+    """
+    Take a model class or model label and return its model label.
+
+    >>> get_model_label(MyModel)
+    "myapp.MyModel"
+
+    >>> get_model_label("myapp.MyModel")
+    "myapp.MyModel"
+    """
+    if isinstance(model, six.string_types):
+        return model
+    else:
+        return "%s.%s" % (
+            model._meta.app_label,
+            model.__name__
+        )


Test patch:
diff --git a/filer/test_utils/test_app/migrations/0001_initial.py b/filer/test_utils/test_app/migrations/0001_initial.py
--- a/filer/test_utils/test_app/migrations/0001_initial.py
+++ b/filer/test_utils/test_app/migrations/0001_initial.py
@@ -2,6 +2,7 @@
 from __future__ import unicode_literals
 
 from django.db import models, migrations
+from filer.settings import FILER_IMAGE_MODEL
 import filer.fields.folder
 import filer.fields.file
 import filer.fields.image
@@ -9,9 +10,15 @@
 
 class Migration(migrations.Migration):
 
-    dependencies = [
-        ('filer', '0001_initial'),
-    ]
+    if FILER_IMAGE_MODEL:
+        dependencies = [
+            ('filer', '0001_initial'),
+            ('custom_image', '0001_initial'),
+        ]
+    else:
+        dependencies = [
+            ('filer', '0001_initial'),
+        ]
 
     operations = [
         migrations.CreateModel(
@@ -20,7 +27,7 @@ class Migration(migrations.Migration):
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('folder', filer.fields.folder.FilerFolderField(related_name='test_folder', to='filer.Folder')),
                 ('general', filer.fields.file.FilerFileField(related_name='test_file', to='filer.File')),
-                ('image', filer.fields.image.FilerImageField(related_name='test_image', to='filer.Image')),
+                ('image', filer.fields.image.FilerImageField(related_name='test_image', to=FILER_IMAGE_MODEL or 'filer.Image')),
             ],
             options={
             },