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

Issue:
Breaking change due to _check_import_id_fields
**Describe the bug**
This is a breaking change in 4.0 vs 3.x. 

In my resource, I've declared a `before_import_row()` to add another column 'Object' to the row, which is used as an identifier. 

```
object = resources.Field(column_name="Object", attribute="object")

def before_import_row(self, row, **kwargs):
....
        row["Object"] = hashlib.sha256((row.get("Object 1 Name") or "").encode()).hexdigest()
...

    class Meta:
...
        import_id_fields = ["time", "object"]
```

However, after upgrading to 4.0.2, the import throws an error

```
File "***/python3.12/site-packages/import_export/resources.py", line 1186, in _check_import_id_fields
raise exceptions.FieldError(
import_export.exceptions.FieldError: The following fields are declared in 'import_id_fields' but are not present in the file headers: Object
```

This check on headers seems not taking into accounts that the rows can change.

**Versions (please complete the following information):**
 - Django Import Export: 4.0.2
 - Python 3.12
 - Django 5.0.6


Discussion:
Can you see if this solves your issue:

```python
    def before_import(self, dataset, **kwargs):
        dataset.headers.append("Object")
        super().before_import(dataset, **kwargs)
```
Thanks I ended up using the `before_import` to modify the whole dataset instead, but seems this behavior should be documented.

Resolving patch:
diff --git a/import_export/resources.py b/import_export/resources.py
--- a/import_export/resources.py
+++ b/import_export/resources.py
@@ -1153,6 +1153,13 @@ def _is_dry_run(self, kwargs):
         return kwargs.get("dry_run", False)
 
     def _check_import_id_fields(self, headers):
+        """
+        Provides a safety check with a meaningful error message for cases where
+        the ``import_id_fields`` declaration contains a field which is not in the
+        dataset.  For most use-cases this is an error, so we detect and raise.
+        There are conditions, such as 'dynamic fields' where this does not apply.
+        See issue 1834 for more information.
+        """
         import_id_fields = list()
         missing_fields = list()
         missing_headers = list()


Test patch:
diff --git a/tests/core/tests/test_resources/test_import_export.py b/tests/core/tests/test_resources/test_import_export.py
--- a/tests/core/tests/test_resources/test_import_export.py
+++ b/tests/core/tests/test_resources/test_import_export.py
@@ -330,6 +330,27 @@ class Meta:
             str(e.exception),
         )
 
+    def test_dynamic_import_id_fields(self):
+        # issue 1834
+        class BookResource(resources.ModelResource):
+            def before_import(self, dataset, **kwargs):
+                # mimic a 'dynamic field' - i.e. append field which exists on
+                # Book model, but not in dataset
+                dataset.headers.append("price")
+                super().before_import(dataset, **kwargs)
+
+            class Meta:
+                model = Book
+                import_id_fields = ("price",)
+
+        self.resource = BookResource()
+        dataset = tablib.Dataset(
+            *[(self.book.pk, "Goldeneye", "ian.fleming@example.com")],
+            headers=["id", "name", "author_email"],
+        )
+        self.resource.import_data(dataset, raise_errors=True)
+        self.assertEqual("Goldeneye", Book.objects.latest("id").name)
+
 
 class ImportWithMissingFields(TestCase):
     # issue 1517