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

Issue:
skip_row doesn't work correctly
Even if I left skip_row=True report_skipped = False and left the others untouched, I couldn’t skip the rows. Until I changed the source, I received errors that my fields were None.
TypeError: __str__ returned non-string (type NoneType)

I sat for hours and corrected everything, tried everything, thinking the problem was in my code. When I corrected this (below), everything worked. I stopped getting errors and it turned out to be skipped according to my logic.

[file [resources.py] line 819.](https://github.com/django-import-export/django-import-export/blob/493ff3c65131814ecd5ef5d12ee547c60bc253fd/import_export/resources.py#L819C27-L819C55) Adding an indent, moved the else inward.

Please look at this case. I have Linux. xlsx file. I import in the admin panel. I did everything as in the documentation. If something is not clear, ask, I will show you where you need it.


Discussion:
Hi - Sorry to hear you spent hours on this, that's annoying.  

I don't know what's going on here.  Can you give us a reproducible test case, or steps to reproduce?  Can you reproduce in the [example app][1]?

The only thing I can think of is that you have a custom `__str__()` definition on your model instance class which may be attempting to cast a None type [here][2]?  Can you confirm?

[1]: https://django-import-export.readthedocs.io/en/latest/installation.html#example-app
[2]: https://github.com/django-import-export/django-import-export/blob/493ff3c65131814ecd5ef5d12ee547c60bc253fd/import_export/results.py#L74
Would you mind translating so that I can answer?  thank you.
I'm sorry ))

Yes, when I removed the str function everything worked correctly. I didn't think there could be a problem with this.

But almost all models implement the str function. For our library to work, do we need to remove this function on each model? Or will we look at alternatives?
np - thanks :)

The [force_str](https://docs.djangoproject.com/en/5.0/ref/utils/#module-django.utils.encoding) call may crash if the `__str__()` method has a property that can be None.  If possible can you post the `__str__()` method you are using so that I can confirm?

I think we need to add a guard against this situation, perhaps with a debug log message.
By the way, if you needed a workaround, you could override the `get_row_result_class()` method in your `Resource` to return a custom implementation which can handle the error.

For example

```python

class MyRowResult(RowResult):
    def add_instance_info(self, instance):
        # add your logic here
        pass

class BookResource(resources.ModelResource):

    def get_row_result_class(self):
        return MyRowResult

    class Meta:
        model = Book

```
To be clear, I only have 6 rows and 8 columns in my file. And when I try to start the import with __str__. I'm getting 993 error with the string None.

My str method returns a field that is in the fields list. And this is really related to force_str

`Line number: 7 - __str__ returned non-string (type NoneType)
None, None, None, None, None, None, None, None, None
Traceback (most recent call last):
File "/home/ikhasanmusaev/PycharmProjects/exbmba_backend/venv/lib/python3.11/site-packages/import_export/resources.py", line 819, in import_row
row_result.add_instance_info(instance)
File "/home/ikhasanmusaev/PycharmProjects/exbmba_backend/venv/lib/python3.11/site-packages/import_export/results.py", line 74, in add_instance_info
self.object_repr = force_str(instance)
^^^^^^^^^^^^^^^^^^^
File "/home/ikhasanmusaev/PycharmProjects/exbmba_backend/venv/lib/python3.11/site-packages/django/utils/encoding.py", line 73, in force_str
s = str(s)
^^^^^^
TypeError: __str__ returned non-string (type NoneType)`


Thanks - I'll take a look
Could you show a real example of where get_row_result_class and add_instance_info are used in practice?
> Thanks - I'll take a look

Thanks!!!
> Could you show a real example of where get_row_result_class and add_instance_info are used in practice?

I don't have an example, but you can add the snippet [above](https://github.com/django-import-export/django-import-export/issues/1763#issuecomment-1971339393) and try it out.  This would be temporary until we can fix the underlying issue.
I realized ) 
I couldn't think of anything other than this:

```python
    ...
    def add_instance_info(self, instance):
        if instance is not None:
            self.object_id = getattr(instance, "pk", None)
            try:
                self.object_repr = force_str(instance)
            except TypeError:
                self.object_repr = instance
```

I would like to draw your attention to one more thing. I don't need to add id to fields. But without it and without `import_id_fields = ['id']` I get errors that id is required and `KeyError: 'id'`. Is it possible to make it so that when the id is not written anywhere, it is automatically filled in the database as a primary key?
>  Is it possible to make it so that when the id is not written anywhere, it is automatically filled in the database as a primary key?

Not sure what you mean here.  If you don't specify `import_id_fields` it should default to 'id' ([docs](https://django-import-export.readthedocs.io/en/4.0.0-rc.0/advanced_usage.html#create-or-update-model-instances)).  If that doesn't solve the problem free to raise as a separate issue.

I also strongly recommend working on [v4](https://pypi.org/project/django-import-export/4.0.0rc0/), as that is going to be a major update soon. 
Thanks! I am thankful!!!
> Not sure what you mean here

I meant to say, don't indicate the id in fields

Resolving patch:
diff --git a/import_export/results.py b/import_export/results.py
--- a/import_export/results.py
+++ b/import_export/results.py
@@ -1,9 +1,13 @@
+import logging
 from collections import OrderedDict
 
 from django.core.exceptions import NON_FIELD_ERRORS
 from django.utils.encoding import force_str
+from django.utils.translation import gettext_lazy as _
 from tablib import Dataset
 
+logger = logging.getLogger(__name__)
+
 
 class Error:
     def __init__(self, error, traceback=None, row=None, number=None):
@@ -110,7 +114,10 @@ def add_instance_info(self, instance):
         if instance is not None:
             # Add object info to RowResult (e.g. for LogEntry)
             self.object_id = getattr(instance, "pk", None)
-            self.object_repr = force_str(instance)
+            try:
+                self.object_repr = force_str(instance)
+            except Exception as e:
+                logger.debug(_("call to force_str() on instance failed: %s" % str(e)))
 
 
 class InvalidRow:


Test patch:
diff --git a/tests/core/tests/test_results.py b/tests/core/tests/test_results.py
--- a/tests/core/tests/test_results.py
+++ b/tests/core/tests/test_results.py
@@ -1,3 +1,5 @@
+from unittest.mock import patch
+
 from core.models import Book
 from django.core.exceptions import ValidationError
 from django.test.testcases import TestCase
@@ -61,6 +63,22 @@ def test_add_instance_info(self):
         self.assertEqual(1, row_result.object_id)
         self.assertEqual("some book", row_result.object_repr)
 
+    @patch("import_export.results.logger")
+    def test_add_instance_info_instance_unserializable(self, mock_logger):
+        # issue 1763
+        class UnserializableBook(object):
+            # will raise TypeError
+            def __str__(self):
+                return None
+
+        row_result = RowResult()
+        row_result.add_instance_info(UnserializableBook())
+        mock_logger.debug.assert_called_with(
+            "call to force_str() on instance failed: "
+            "__str__ returned non-string (type NoneType)"
+        )
+        self.assertEqual(None, row_result.object_repr)
+
     def test_is_new(self):
         row_result = RowResult()
         self.assertFalse(row_result.is_new())