django-import-export / django-import-export

Django application and library for importing and exporting data with admin integration.

Home Page:https://django-import-export.readthedocs.org/en/latest/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cannot import datetime field having db_default attribute (new in Django 5.0)

ipamo opened this issue · comments

Describe the bug
Trying to import datetime fields having db_default attribute produce AttributeError: 'DatabaseDefault' object has no attribute 'utcoffset'.

To Reproduce
Steps to reproduce the behavior:

  1. Configure a model with field DateTimeField(auto_now_add=True, db_default=Now())
  2. Try to import data to this model in the admin site

See this dedicated repository to reproduce the issue.

Versions

  • Windows 10 22H2 (build 19045.3930)
  • Python 3.11.5
  • PostgreSQL 15.4
  • Django 5.0.1
  • Psycopg 3.1.17
  • Django-import-export 3.3.6

Traceback

Traceback (most recent call last):
File "…\import-db-default\.venv\Lib\site-packages\import_export\resources.py", line 785, in import_row
diff = self.get_diff_class()(self, original, new)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "…\import-db-default\.venv\Lib\site-packages\import_export\resources.py", line 259, in __init__
self.left = self._export_resource_fields(resource, instance)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "…\import-db-default\.venv\Lib\site-packages\import_export\resources.py", line 280, in _export_resource_fields
return [
^
File "…\import-db-default\.venv\Lib\site-packages\import_export\resources.py", line 281, in <listcomp>
resource.export_field(f, instance) if instance else ""
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "…\import-db-default\.venv\Lib\site-packages\import_export\resources.py", line 1066, in export_field
return field.export(obj)
^^^^^^^^^^^^^^^^^
File "…\import-db-default\.venv\Lib\site-packages\import_export\fields.py", line 148, in export
return self.widget.render(value, obj)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "…\import-db-default\.venv\Lib\site-packages\import_export\widgets.py", line 266, in render
value = timezone.localtime(value)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "…\import-db-default\.venv\Lib\site-packages\django\utils\timezone.py", line 182, in localtime
if is_naive(value):
^^^^^^^^^^^^^^^
File "…\import-db-default\.venv\Lib\site-packages\django\utils\timezone.py", line 234, in is_naive
return value.utcoffset() is None
^^^^^^^^^^^^^^^
AttributeError: 'DatabaseDefault' object has no attribute 'utcoffset'

Thanks for raising. I have found that you can reproduce the same issue in the shell with:

>>> from django.db.models.functions.datetime import Now
>>> from django.utils import timezone
>>> timezone.localtime(Now())
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/matthew/.virtualenvs/django-import-export/lib/python3.11/site-packages/django/utils/timezone.py", line 182, in localtime
    if is_naive(value):
       ^^^^^^^^^^^^^^^
  File "/Users/matthew/.virtualenvs/django-import-export/lib/python3.11/site-packages/django/utils/timezone.py", line 234, in is_naive
    return value.utcoffset() is None
           ^^^^^^^^^^^^^^^

The interface of timezone.localtime() states that a datetime instance is passed, therefore it's not valid to pass a Now instance.

The issue is that the 'diff' process used to show the differences in the import uses DateTimeWidget which is being asked to render a Now instance (it assumes it is being passed a date). Hence it crashes. This is only occurring on the import of new objects, so I expect that the issue occurs when diffing against a 'new' instance.

I added a test here.

I don't know what the solution is at the moment.

As a workaround, you can avoid the crash during import if you set skip_diff to True on the resource:

class BookResource(ModelResource):
    class Meta:
        model = Book
        skip_diff = True

This means that you would not be able to see diffs on import.

Thank you very much for the explanation and workaround.

np - I'll add a fix for this when I get a chance.

I suggest that the best way to fix this would be for each widget render() method to have a type check which checks that the value passed corresponds to the underlying type, if not then a TypeError is raised. Callers further up the stack can then catch this error and handle accordingly.