Andrew Godwin avatar Andrew Godwin committed 2831038

Clean up on_delete support some more, remove the conditional import bit

Comments (0)

Files changed (1)

south/modelsinspector.py

 NOISY = False
 
 try:
-    from django.db.models import CASCADE, PROTECT, SET, SET_NULL, SET_DEFAULT, DO_NOTHING
-    on_delete_is_available = True
-except ImportError:
-    on_delete_is_available = False
-try:
     from django.utils import timezone
 except ImportError:
     timezone = False
 
-if on_delete_is_available:
-    def convert_on_delete_handler(value):
-        django_db_models_module = 'models' # relative to standard import 'django.db'
-        if django_db_models_module:
-            if value in (CASCADE, PROTECT, DO_NOTHING, SET_DEFAULT):
-                # straightforward functions
-                return '%s.%s' % (django_db_models_module, value.__name__)
-            else:
-                # This is totally dependent on the implementation of django.db.models.deletion.SET
-                func_name = getattr(value, '__name__', None)
-                if func_name == 'set_on_delete':
-                    # we must inspect the function closure to see what parameters were passed in
-                    closure_contents = value.func_closure[0].cell_contents
-                    if closure_contents is None:
-                        return "%s.SET_NULL" % (django_db_models_module)
-                    # simple function we can perhaps cope with:
-                    elif hasattr(closure_contents, '__call__'):
-                        # module_name = getattr(closure_contents, '__module__', None)
-                        # inner_func_name = getattr(closure_contents, '__name__', None)
-                        # if inner_func_name:
-                            # TODO there is no way of checking that module_name matches the
-                            # model file, which is the only code that will be imported in
-                            # the Fake ORM. Any other functions won't be available.
-                            # TODO this doesn't work anyway yet as even the app's models
-                            # file is not imported, contrary to the coments in
-                            # orm.LazyFakeORM.eval_in_context, which implies that
-                            # migrations are expected to import that.
-                            # return "%s.SET(%s)" % (django_db_models_module, inner_func_name)
-                        raise ValueError("Function for on_delete could not be serialized.")
-                    else:
-                        # an actual value rather than a sentinel function - insanity
-                        raise ValueError("on_delete=SET with a model instance is not supported.")
-                        
-        raise ValueError("%s was not recognized as a valid model deletion handler. Possible values: %s." % (value, ', '.join(f.__name__ for f in (CASCADE, PROTECT, SET, SET_NULL, SET_DEFAULT, DO_NOTHING))))
+
+# Define any converter functions first to prevent NameErrors
+
+def convert_on_delete_handler(value):
+    django_db_models_module = 'models'  # relative to standard import 'django.db'
+    if hasattr(models, "PROTECT"):
+        if value in (models.CASCADE, models.PROTECT, models.DO_NOTHING, models.SET_DEFAULT):
+            # straightforward functions
+            return '%s.%s' % (django_db_models_module, value.__name__)
+        else:
+            # This is totally dependent on the implementation of django.db.models.deletion.SET
+            func_name = getattr(value, '__name__', None)
+            if func_name == 'set_on_delete':
+                # we must inspect the function closure to see what parameters were passed in
+                closure_contents = value.func_closure[0].cell_contents
+                if closure_contents is None:
+                    return "%s.SET_NULL" % (django_db_models_module)
+                # simple function we can perhaps cope with:
+                elif hasattr(closure_contents, '__call__'):
+                    raise ValueError("South does not support on_delete with SET(function) as values.")
+                else:
+                    # Attempt to serialise the value
+                    return "%s.SET(%s)" % (django_db_models_module, value_clean(closure_contents))
+        raise ValueError("%s was not recognized as a valid model deletion handler. Possible values: %s." % (value, ', '.join(f.__name__ for f in (models.CASCADE, models.PROTECT, models.SET, models.SET_NULL, models.SET_DEFAULT, models.DO_NOTHING))))
+    else:
+        raise ValueError("on_delete argument encountered in Django version that does not support it")
 
 # Gives information about how to introspect certain fields.
 # This is a list of triples; the first item is a list of fields it applies to,
     (
         (models.ForeignKey, models.OneToOneField),
         [],
-        dict ( [
+        dict([
             ("to", ["rel.to", {}]),
             ("to_field", ["rel.field_name", {"default_attr": "rel.to._meta.pk.name"}]),
             ("related_name", ["rel.related_name", {"default": None}]),
             ("db_index", ["db_index", {"default": True}]),
-            ] + 
-            # on_delete was added in Django 1.3, only use it when available
-            (on_delete_is_available \
-                and [("on_delete", ["rel.on_delete", {"default": CASCADE, "is_django_function": True, "converter": convert_on_delete_handler, }])] \
-                or []))
+            ("on_delete", ["rel.on_delete", {"default": getattr(models, "CASCADE", None), "is_django_function": True, "converter": convert_on_delete_handler}])
+        ])
     ),
     (
         (models.ManyToManyField,),
     allowed_fields.extend(patterns)
     introspection_details.extend(rules)
 
+
 def add_ignored_fields(patterns):
     "Allows you to add some ignore field patterns."
     assert isinstance(patterns, (list, tuple))
     ignored_fields.extend(patterns)
     
+
 def can_ignore(field):
     """
     Returns True if we know for certain that we can ignore this field, False
             return True
     return False
 
+
 def can_introspect(field):
     """
     Returns True if we are allowed to introspect this field, False otherwise.
     else:
         return repr(value)
 
+
 def introspector(field):
     """
     Given a field, introspects its definition triple.
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.