Repository: django-import-export/django-import-export

Issue:
1.8 compatibility
```
  File "/Users/syabro/.envs/homeowners/lib/python2.7/site-packages/import_export/resources.py", line 16, in <module>
    from django.db.models.related import RelatedObject
ImportError: No module named related
```

because

> The django.db.models.related module has been removed and the Field.related attribute will be removed in Django 2.0.

https://docs.djangoproject.com/en/dev/releases/1.8/#model-field-related



Discussion:
Thanks for contributing. I did not check Django 1.8 yet. Would you like to write a patch for this? (please note that django 1.4.x should stay supported)

We'll upgrade our project to 1.8 when beta will be released around 15 Feb. If it'll be open still — will do.

I just ran into this too... Has any progress been made yet?


Resolving patch:
diff --git a/import_export/django_compat.py b/import_export/django_compat.py
new file mode 100644
--- /dev/null
+++ b/import_export/django_compat.py
@@ -0,0 +1,23 @@
+from __future__ import unicode_literals
+
+from django.db import transaction
+
+# transaction management for Django < 1.6
+
+def atomic(*args, **kw):
+    def noop_decorator(func):
+        return func  # pass through
+
+    return noop_decorator
+
+def savepoint(*args, **kwargs):
+    transaction.enter_transaction_management()
+    transaction.managed(True)
+
+def savepoint_rollback(*args, **kwargs):
+    transaction.rollback()
+    transaction.leave_transaction_management()
+
+def savepoint_commit(*args, **kwargs):
+    transaction.commit()
+    transaction.leave_transaction_management()
diff --git a/import_export/resources.py b/import_export/resources.py
--- a/import_export/resources.py
+++ b/import_export/resources.py
@@ -8,12 +8,11 @@
 import tablib
 from diff_match_patch import diff_match_patch
 
+from django import VERSION
 from django.utils.safestring import mark_safe
 from django.utils import six
-from django.db import transaction
 from django.db.models.fields import FieldDoesNotExist
 from django.db.models.query import QuerySet
-from django.db.models.related import RelatedObject
 from django.conf import settings
 
 from .results import Error, Result, RowResult
@@ -23,6 +22,18 @@
     ModelInstanceLoader,
 )
 
+try:
+    from django.db.transaction import atomic, savepoint, savepoint_rollback, savepoint_commit  # noqa
+except ImportError:
+    from .django_compat import atomic, savepoint, savepoint_rollback, savepoint_commit  # noqa
+
+
+if VERSION < (1, 8):
+    from django.db.models.related import RelatedObject
+    ForeignObjectRel = RelatedObject
+else:
+    from django.db.models.fields.related import ForeignObjectRel
+    RelatedObject = None
 
 try:
     from django.utils.encoding import force_text
@@ -279,6 +290,7 @@ def before_import(self, dataset, dry_run):
         """
         pass
 
+    @atomic()
     def import_data(self, dataset, dry_run=False, raise_errors=False,
             use_transactions=None):
         """
@@ -299,8 +311,7 @@ def import_data(self, dataset, dry_run=False, raise_errors=False,
             # when transactions are used we want to create/update/delete object
             # as transaction will be rolled back if dry_run is set
             real_dry_run = False
-            transaction.enter_transaction_management()
-            transaction.managed(True)
+            sp1 = savepoint()
         else:
             real_dry_run = dry_run
 
@@ -311,8 +322,7 @@ def import_data(self, dataset, dry_run=False, raise_errors=False,
             result.base_errors.append(Error(repr(e), tb_info))
             if raise_errors:
                 if use_transactions:
-                    transaction.rollback()
-                    transaction.leave_transaction_management()
+                    savepoint_rollback(sp1)
                 raise
 
         instance_loader = self._meta.instance_loader_class(self, dataset)
@@ -354,8 +364,7 @@ def import_data(self, dataset, dry_run=False, raise_errors=False,
                 row_result.errors.append(Error(e, tb_info))
                 if raise_errors:
                     if use_transactions:
-                        transaction.rollback()
-                        transaction.leave_transaction_management()
+                        savepoint_rollback(sp1)
                     six.reraise(*sys.exc_info())
             if (row_result.import_type != RowResult.IMPORT_TYPE_SKIP or
                         self._meta.report_skipped):
@@ -363,10 +372,9 @@ def import_data(self, dataset, dry_run=False, raise_errors=False,
 
         if use_transactions:
             if dry_run or result.has_errors():
-                transaction.rollback()
+                savepoint_rollback(sp1)
             else:
-                transaction.commit()
-            transaction.leave_transaction_management()
+                savepoint_commit(sp1)
 
         return result
 
@@ -462,14 +470,18 @@ def __new__(cls, name, bases, attrs):
                             # We're not at the last attribute yet, so check that
                             # we're looking at a relation, and move on to the
                             # next model.
-                            if isinstance(f, RelatedObject):
-                                model = f.model
+                            if isinstance(f, ForeignObjectRel):
+                                if RelatedObject is None:
+                                    model = f.related_model
+                                else:
+                                    # Django < 1.8
+                                    model = f.model
                             else:
                                 if f.rel is None:
                                     raise KeyError('%s is not a relation' % verbose_path)
                                 model = f.rel.to
 
-                    if isinstance(f, RelatedObject):
+                    if isinstance(f, ForeignObjectRel):
                         f = f.field
 
                     field = new_class.field_from_django_field(field_name, f,


Test patch: