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

Issue:
import of m2m relations failing in django 2
TypeError: Direct assignment to the forward side of a many-to-many set is prohibited. Use m2m_field.set() instead.



Discussion:

Resolving patch:
diff --git a/import_export/fields.py b/import_export/fields.py
--- a/import_export/fields.py
+++ b/import_export/fields.py
@@ -102,7 +102,7 @@ def get_value(self, obj):
             value = value()
         return value
 
-    def save(self, obj, data):
+    def save(self, obj, data, is_m2m=False):
         """
         If this field is not declared readonly, the object's attribute will
         be set to the value returned by :meth:`~import_export.fields.Field.clean`.
@@ -113,7 +113,10 @@ def save(self, obj, data):
                 obj = getattr(obj, attr, None)
             cleaned = self.clean(data)
             if cleaned is not None or self.saves_null_values:
-                setattr(obj, attrs[-1], cleaned)
+                if not is_m2m:
+                    setattr(obj, attrs[-1], cleaned)
+                else:
+                    getattr(obj, attrs[-1]).set(cleaned)
 
     def export(self, obj):
         """
diff --git a/import_export/resources.py b/import_export/resources.py
--- a/import_export/resources.py
+++ b/import_export/resources.py
@@ -316,13 +316,13 @@ def after_delete_instance(self, instance, dry_run):
         """
         pass
 
-    def import_field(self, field, obj, data):
+    def import_field(self, field, obj, data, is_m2m=False):
         """
         Calls :meth:`import_export.fields.Field.save` if ``Field.attribute``
         and ``Field.column_name`` are found in ``data``.
         """
         if field.attribute and field.column_name in data:
-            field.save(obj, data)
+            field.save(obj, data, is_m2m)
 
     def get_import_fields(self):
         return self.get_fields()
@@ -351,7 +351,7 @@ def save_m2m(self, obj, data, using_transactions, dry_run):
             for field in self.get_import_fields():
                 if not isinstance(field.widget, widgets.ManyToManyWidget):
                     continue
-                self.import_field(field, obj, data)
+                self.import_field(field, obj, data, True)
 
     def for_delete(self, row, instance):
         """


Test patch:
diff --git a/tests/core/fixtures/book.json b/tests/core/fixtures/book.json
new file mode 100644
--- /dev/null
+++ b/tests/core/fixtures/book.json
@@ -0,0 +1,24 @@
+[
+    {
+        "model": "core.author",
+        "pk": 11,
+        "fields": {
+            "name": "George R. R. Martin",
+            "birthday": "1948-09-20"
+        }
+    },
+    {
+        "model": "core.book",
+        "pk": 11,
+        "fields": {
+            "name": "A Game of Thrones",
+            "author": 11,
+            "author_email": "martin@got.com",
+            "imported": false,
+            "published": "1996-08-01",
+            "published_time": "21:00",
+            "price": 25.0,
+            "categories": [1]
+        }
+    }
+]
\ No newline at end of file
diff --git a/tests/core/tests/resources_tests.py b/tests/core/tests/resources_tests.py
--- a/tests/core/tests/resources_tests.py
+++ b/tests/core/tests/resources_tests.py
@@ -834,7 +834,7 @@ def test_arrayfield(self):
 
 
 class ManyRelatedManagerDiffTest(TestCase):
-    fixtures = ["category"]
+    fixtures = ["category", "book"]
 
     def setUp(self):
         pass