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

Issue:
add logging to aid with debugging missing data
If a user defines a field which is not present in the source import file, then this gets silently ignored, and nothing is imported.

For example:

```python
banana = fields.Field(
    attribute='banana',
    widget=CustomForeignKeyWidget(Banana, field="name__iexact"))
```

If there is logic in `CustomForeignKeyWidget`, then this will never be called and it will be confusing.   I suggest to add logging [here](https://github.com/django-import-export/django-import-export/blob/905839290016850327658bbee790314d4854f8a6/import_export/resources.py#L543),  so that there is some output for the users.

[Related SO question](https://stackoverflow.com/q/74647054/39296)



Discussion:

Resolving patch:
diff --git a/import_export/resources.py b/import_export/resources.py
--- a/import_export/resources.py
+++ b/import_export/resources.py
@@ -647,8 +647,16 @@ def import_field(self, field, instance, row, is_m2m=False, **kwargs):
         :param \**kwargs:
             See :meth:`import_row`
         """
-        if field.attribute and field.column_name in row:
-            field.save(instance, row, is_m2m, **kwargs)
+        if not field.attribute:
+            logger.debug(f"skipping field '{field}' - field attribute is not defined")
+            return
+        if field.column_name not in row:
+            logger.debug(
+                f"skipping field '{field}' "
+                f"- column name '{field.column_name}' is not present in row"
+            )
+            return
+        field.save(instance, row, is_m2m, **kwargs)
 
     def get_import_fields(self):
         return [self.fields[f] for f in self.get_import_order()]


Test patch:
diff --git a/tests/core/tests/test_resources.py b/tests/core/tests/test_resources.py
--- a/tests/core/tests/test_resources.py
+++ b/tests/core/tests/test_resources.py
@@ -2271,8 +2271,7 @@ class Meta:
         resource = _BookResource()
         with mock.patch("logging.Logger.debug") as mock_exception:
             resource.import_data(self.dataset)
-            mock_exception.assert_called_with(e, exc_info=mock.ANY)
-            self.assertEqual(1, mock_exception.call_count)
+            mock_exception.assert_called_with(e, exc_info=e)
 
     @mock.patch("core.models.Book.objects.bulk_create")
     def test_bulk_create_raises_exception(self, mock_bulk_create):
@@ -2472,8 +2471,7 @@ class Meta:
         resource = _BookResource()
         with mock.patch("logging.Logger.debug") as mock_exception:
             resource.import_data(self.dataset)
-            mock_exception.assert_called_with(e, exc_info=mock.ANY)
-            self.assertEqual(1, mock_exception.call_count)
+            mock_exception.assert_called_with(e, exc_info=e)
 
     @mock.patch("core.models.Book.objects.bulk_update")
     def test_bulk_update_raises_exception(self, mock_bulk_update):
@@ -2898,3 +2896,40 @@ class Meta:
             "in the dataset: id, author_email",
         ):
             self.resource.import_data(dataset)
+
+
+class ImportWithMissingFields(TestCase):
+    # issue 1517
+
+    @patch("import_export.resources.logger")
+    @patch("import_export.fields.Field.save")
+    def test_import_with_missing_instance_attribute(self, mock_field_save, mock_logger):
+        class _BookResource(resources.ModelResource):
+            name = fields.Field(column_name="name")
+
+            class Meta:
+                model = Book
+
+        dataset = tablib.Dataset(*[(1, "Some book")], headers=["id", "name"])
+        self.resource = _BookResource()
+        result = self.resource.import_data(dataset)
+        self.assertFalse(result.has_errors())
+        target = (
+            "skipping field '<import_export.fields.Field: name>' "
+            "- field attribute is not defined"
+        )
+        mock_logger.debug.assert_any_call(target)
+        self.assertEqual(1, mock_field_save.call_count)
+
+    @patch("import_export.resources.logger")
+    @patch("import_export.fields.Field.save")
+    def test_import_with_missing_field_in_row(self, mock_field_save, mock_logger):
+        dataset = tablib.Dataset(*[(1, "Some book")], headers=["id", "name"])
+        self.resource = BookResource()
+        result = self.resource.import_data(dataset)
+        self.assertFalse(result.has_errors())
+        mock_logger.debug.assert_any_call(
+            "skipping field '<import_export.fields.Field: author_email>' "
+            "- column name 'author_email' is not present in row"
+        )
+        self.assertEqual(2, mock_field_save.call_count)