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

Issue:
`resource_class` was deprecated and removed, but it still works
**Describe the bug**

`resource_class` was removed in v4 in favour of `resource_classes`.  However the following declaration still works:

```
class BookAdmin(ImportExportModelAdmin):
    list_display = ("name", "author", "added")
    list_filter = ["categories", "author"]
    resource_class = BookResource
    change_list_template = "core/admin/change_list.html"
```

We probably need to re-add the deprecation and ensure it is removed in a future release.

**To Reproduce**

update `core/admin.py` with the above snippet and you will find you can import a file ok.



Discussion:
FYI: I think this caused a pretty nasty bug for me, because I think mistakenly using the deprecated `resource_class` prevents the `class Meta:` options from loading without giving a clear error message

Resolving patch:
diff --git a/import_export/mixins.py b/import_export/mixins.py
--- a/import_export/mixins.py
+++ b/import_export/mixins.py
@@ -1,4 +1,5 @@
 import logging
+import warnings
 from warnings import warn
 
 from django.conf import settings
@@ -20,6 +21,7 @@ class BaseImportExportMixin:
     interface.
     """
 
+    resource_class = None
     resource_classes = []
 
     @property
@@ -47,11 +49,30 @@ def get_resource_classes(self, request):
         :param request: The request object.
         :returns: The Resource classes.
         """
-        if not self.resource_classes:
+        if self.resource_classes and self.resource_class:
+            raise Exception(
+                "Only one of 'resource_class' and 'resource_classes' can be set"
+            )
+        if hasattr(self, "get_resource_class"):
+            warnings.warn(
+                "The 'get_resource_class()' method has been deprecated. "
+                "Please implement the new 'get_resource_classes()' method",
+                DeprecationWarning,
+            )
+            return [self.get_resource_class()]
+        if self.resource_class:
+            warnings.warn(
+                "The 'resource_class' field has been deprecated. "
+                "Please implement the new 'resource_classes' field",
+                DeprecationWarning,
+            )
+        if not self.resource_classes and not self.resource_class:
             return [modelresource_factory(self.model)]
-        return self.resource_classes
+        if self.resource_classes:
+            return self.resource_classes
+        return [self.resource_class]
 
-    def get_resource_kwargs(self, request, **kwargs):
+    def get_resource_kwargs(self, request, *args, **kwargs):
         """
         Return the kwargs which are to be passed to the Resource constructor.
         Can be overridden to provide additional kwarg params.
@@ -84,6 +105,13 @@ def get_import_resource_classes(self, request):
         :param request: The request object.
         Returns ResourceClass subscriptable (list, tuple, ...) to use for import.
         """
+        if hasattr(self, "get_import_resource_class"):
+            warnings.warn(
+                "The 'get_import_resource_class()' method has been deprecated. "
+                "Please implement the new 'get_import_resource_classes()' method",
+                DeprecationWarning,
+            )
+            return [self.get_import_resource_class()]
         resource_classes = self.get_resource_classes(request)
         self.check_resource_classes(resource_classes)
         return resource_classes
@@ -129,6 +157,13 @@ def get_export_resource_classes(self, request):
         :param request: The request object.
         :returns: The Resource classes.
         """
+        if hasattr(self, "get_export_resource_class"):
+            warnings.warn(
+                "The 'get_export_resource_class()' method has been deprecated. "
+                "Please implement the new 'get_export_resource_classes()' method",
+                DeprecationWarning,
+            )
+            return [self.get_export_resource_class()]
         resource_classes = self.get_resource_classes(request)
         self.check_resource_classes(resource_classes)
         return resource_classes


Test patch:
diff --git a/tests/core/tests/test_mixins.py b/tests/core/tests/test_mixins.py
--- a/tests/core/tests/test_mixins.py
+++ b/tests/core/tests/test_mixins.py
@@ -182,6 +182,62 @@ def test_get_export_resource_kwargs_calls_self_get_resource_kwargs(self):
         admin.get_export_resource_kwargs(self.request)
         self.assertEqual(1, admin.call_count)
 
+    class BaseModelResourceClassTest(mixins.BaseImportMixin, mixins.BaseExportMixin):
+        resource_class = resources.Resource
+        export_call_count = 0
+        import_call_count = 0
+
+        def get_export_resource_class(self):
+            self.export_call_count += 1
+
+        def get_import_resource_class(self):
+            self.import_call_count += 1
+
+    def test_deprecated_resource_class_raises_warning(self):
+        """Test that the mixin throws error if user didn't
+        migrate to resource_classes"""
+        admin = self.BaseModelResourceClassTest()
+        with self.assertWarnsRegex(
+            DeprecationWarning,
+            r"^The 'get_export_resource_class\(\)' method has been deprecated. "
+            r"Please implement the new 'get_export_resource_classes\(\)' method$",
+        ):
+            admin.get_export_resource_classes(self.request)
+
+        with self.assertWarnsRegex(
+            DeprecationWarning,
+            r"^The 'get_import_resource_class\(\)' method has been deprecated. "
+            r"Please implement the new 'get_import_resource_classes\(\)' method$",
+        ):
+            admin.get_import_resource_classes(self.request)
+
+        with self.assertWarnsRegex(
+            DeprecationWarning,
+            r"^The 'resource_class' field has been deprecated. "
+            r"Please implement the new 'resource_classes' field$",
+        ):
+            self.assertEqual(
+                admin.get_resource_classes(self.request), [resources.Resource]
+            )
+
+        self.assertEqual(1, admin.export_call_count)
+        self.assertEqual(1, admin.import_call_count)
+
+    class BaseModelGetExportResourceClassTest(mixins.BaseExportMixin):
+        def get_resource_class(self):
+            pass
+
+    def test_deprecated_get_resource_class_raises_warning(self):
+        """Test that the mixin throws error if user
+        didn't migrate to resource_classes"""
+        admin = self.BaseModelGetExportResourceClassTest()
+        with self.assertWarnsRegex(
+            DeprecationWarning,
+            r"^The 'get_resource_class\(\)' method has been deprecated. "
+            r"Please implement the new 'get_resource_classes\(\)' method$",
+        ):
+            admin.get_resource_classes(self.request)
+
     class BaseModelAdminFaultyResourceClassesTest(mixins.BaseExportMixin):
         resource_classes = resources.Resource
 
@@ -199,6 +255,14 @@ class BaseModelAdminBothResourceTest(mixins.BaseExportMixin):
         resource_class = resources.Resource
         resource_classes = [resources.Resource]
 
+    def test_both_resource_class_raises_exception(self):
+        """Test fallback mechanism to old get_export_resource_class() method"""
+        admin = self.BaseModelAdminBothResourceTest()
+        with self.assertRaisesRegex(
+            Exception, "Only one of 'resource_class' and 'resource_classes' can be set"
+        ):
+            admin.get_export_resource_classes(self.request)
+
     class BaseModelExportChooseTest(mixins.BaseExportMixin):
         resource_classes = [resources.Resource, FooResource]
 
@@ -231,6 +295,34 @@ def test_choose_import_resource_class(self, form):
         form.cleaned_data = {"resource": 1}
         self.assertEqual(admin.choose_import_resource_class(form, request), FooResource)
 
+    class BaseModelResourceClassOldTest(mixins.BaseImportMixin, mixins.BaseExportMixin):
+        def get_resource_class(self):
+            return FooResource
+
+    def test_get_resource_class_old(self):
+        """
+        Test that if only the old get_resource_class() method is defined,
+        the get_export_resource_classes() and get_import_resource_classes()
+        still return list of resources.
+        """
+        admin = self.BaseModelResourceClassOldTest()
+        with self.assertWarnsRegex(
+            DeprecationWarning,
+            r"^The 'get_resource_class\(\)' method has been deprecated. "
+            r"Please implement the new 'get_resource_classes\(\)' method$",
+        ):
+            self.assertEqual(
+                admin.get_export_resource_classes(self.request), [FooResource]
+            )
+        with self.assertWarnsRegex(
+            DeprecationWarning,
+            r"^The 'get_resource_class\(\)' method has been deprecated. "
+            r"Please implement the new 'get_resource_classes\(\)' method$",
+        ):
+            self.assertEqual(
+                admin.get_import_resource_classes(self.request), [FooResource]
+            )
+
 
 class BaseExportMixinTest(TestCase):
     class TestBaseExportMixin(mixins.BaseExportMixin):