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

Issue:
Modify `ForeignKeyWidget` to make it easier to override args
This is a potential enhancement

If you want to load a related model using a mixture of fields in `ForeignKeyWidget`, you have to override the whole `clean()` method.

If there was a `get_lookup_kwargs(value, row=None, **kwargs)` method, then users would only need to override this method and not the whole `clean()` method.

e.g. see [this SO question](https://stackoverflow.com/q/74647054/39296)



Discussion:

Resolving patch:
diff --git a/import_export/widgets.py b/import_export/widgets.py
--- a/import_export/widgets.py
+++ b/import_export/widgets.py
@@ -422,8 +422,12 @@ def get_queryset(self, value, row, *args, **kwargs):
         Overwrite this method if you want to limit the pool of objects from
         which the related object is retrieved.
 
-        :param value: The field's value in the datasource.
-        :param row: The datasource's current row.
+        :param value: The field's value in the dataset.
+        :param row: The dataset's current row.
+        :param \\*args:
+            Optional args.
+        :param \\**kwargs:
+            Optional kwargs.
 
         As an example; if you'd like to have ForeignKeyWidget look up a Person
         by their pre- **and** lastname column, you could subclass the widget
@@ -439,6 +443,16 @@ def get_queryset(self, value, row, *args, **kwargs):
         return self.model.objects.all()
 
     def clean(self, value, row=None, **kwargs):
+        """
+        Return a single Foreign Key instance derived from the args.
+        ``None`` can be returned if the value passed is a null value.
+
+        :param value: The field's value in the dataset.
+        :param row: The dataset's current row.
+        :param \\**kwargs:
+            Optional kwargs.
+        :raises: ``ObjectDoesNotExist`` if no valid instance can be found.
+        """
         val = super().clean(value)
         if val:
             if self.use_natural_foreign_keys:
@@ -446,10 +460,23 @@ def clean(self, value, row=None, **kwargs):
                 value = json.loads(value)
                 return self.model.objects.get_by_natural_key(*value)
             else:
-                return self.get_queryset(value, row, **kwargs).get(**{self.field: val})
+                lookup_kwargs = self.get_lookup_kwargs(value, row, **kwargs)
+                return self.get_queryset(value, row, **kwargs).get(**lookup_kwargs)
         else:
             return None
 
+    def get_lookup_kwargs(self, value, row, **kwargs):
+        """
+        Returns the key value pairs used to identify a model instance.
+        Override this to customize instance look up.
+
+        :param value: The field's value in the dataset.
+        :param row: The dataset's current row.
+        :param \\**kwargs:
+            Optional kwargs.
+        """
+        return {self.field: value}
+
     def render(self, value, obj=None):
         if value is None:
             return ""


Test patch:
diff --git a/tests/core/migrations/0013_alter_author_birthday.py b/tests/core/migrations/0013_alter_author_birthday.py
new file mode 100644
--- /dev/null
+++ b/tests/core/migrations/0013_alter_author_birthday.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.3 on 2023-10-11 03:54
+
+import django.utils.timezone
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("core", "0012_delete_legacybook"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="author",
+            name="birthday",
+            field=models.DateTimeField(default=django.utils.timezone.now),
+        ),
+    ]
diff --git a/tests/core/models.py b/tests/core/models.py
--- a/tests/core/models.py
+++ b/tests/core/models.py
@@ -4,6 +4,7 @@
 
 from django.core.exceptions import ValidationError
 from django.db import models
+from django.utils import timezone
 
 
 class AuthorManager(models.Manager):
@@ -24,7 +25,7 @@ class Author(models.Model):
     objects = AuthorManager()
 
     name = models.CharField(max_length=100)
-    birthday = models.DateTimeField(auto_now_add=True)
+    birthday = models.DateTimeField(default=timezone.now)
 
     def natural_key(self):
         """
diff --git a/tests/core/tests/test_widgets.py b/tests/core/tests/test_widgets.py
--- a/tests/core/tests/test_widgets.py
+++ b/tests/core/tests/test_widgets.py
@@ -483,6 +483,19 @@ def get_queryset(self, value, row):
         with self.assertRaises(TypeError):
             birthday_widget.clean("Foo", row=row_dict, row_number=1)
 
+    def test_lookup_multiple_columns(self):
+        # issue 1516 - override the values used to lookup an entry
+        class BirthdayWidget(widgets.ForeignKeyWidget):
+            def get_lookup_kwargs(self, value, row, *args, **kwargs):
+                return {"name": row["name"], "birthday": row["birthday"]}
+
+        target_author = Author.objects.create(name="James Joyce", birthday="1882-02-02")
+        row_dict = {"name": "James Joyce", "birthday": "1882-02-02"}
+        birthday_widget = BirthdayWidget(Author, "name")
+        # prove that the overridden kwargs identify a row
+        res = birthday_widget.clean("non-existent name", row=row_dict)
+        self.assertEqual(target_author, res)
+
     def test_render_handles_value_error(self):
         class TestObj(object):
             @property