InvenTree icon indicating copy to clipboard operation
InvenTree copied to clipboard

Updating database version

Open LastEdit opened this issue 1 month ago • 1 comments

Please verify that this bug has NOT been raised before.

  • [x] I checked and didn't find a similar issue

Describe the bug*

I updated to 1.1.2 recently and am currently using postgres v13 in my yml. I see the newest yml is running v17 and want to update my database.

I have tried a few times to try to restore running fresh containers running v17 and importing the json from my export, but it's not working for me and I am getting an error.

The database is empty before I start the import, as in, I can't even log in since there are no users. The error seems to be related to duplicate keys for a user.

Should import-records work when trying to update the database like this? I believe it should since it is database agnostic.

Is there a best way to do this?

Steps to Reproduce

Create new containers with empty v17 db docker compose run --rm inventree-server invoke update docker compose run inventree-server invoke import-records -c -f data/backup/data.json unable to finish

Expected behaviour

The json import to work

Deployment Method

Docker

Version Information

1.1.2

Try to reproduce on the demo site

I did not try to reproduce

Is the bug reproducible on the demo site?

Not reproducible

Relevant log output

Traceback (most recent call last):
  File "/root/.local/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/lib/python3.11/site-packages/psycopg/cursor.py", line 97, in execute
    raise ex.with_traceback(None)
psycopg.errors.UniqueViolation: duplicate key value violates unique constraint "users_userprofile_user_id_key"
DETAIL:  Key (user_id)=(2) already exists.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/inventree/src/backend/InvenTree/manage.py", line 24, in <module>
    main()
  File "/home/inventree/src/backend/InvenTree/manage.py", line 20, in main
    execute_from_command_line(sys.argv)
  File "/root/.local/lib/python3.11/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/root/.local/lib/python3.11/site-packages/django/core/management/__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/root/.local/lib/python3.11/site-packages/django/core/management/base.py", line 412, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/root/.local/lib/python3.11/site-packages/django/core/management/base.py", line 458, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/lib/python3.11/site-packages/django/core/management/commands/loaddata.py", line 102, in handle
    self.loaddata(fixture_labels)
  File "/root/.local/lib/python3.11/site-packages/django/core/management/commands/loaddata.py", line 163, in loaddata
    self.load_label(fixture_label)
  File "/root/.local/lib/python3.11/site-packages/django/core/management/commands/loaddata.py", line 253, in load_label
    if self.save_obj(obj):
       ^^^^^^^^^^^^^^^^^^
  File "/root/.local/lib/python3.11/site-packages/django/core/management/commands/loaddata.py", line 209, in save_obj
    obj.save(using=self.using)
  File "/root/.local/lib/python3.11/site-packages/django/core/serializers/base.py", line 288, in save
    models.Model.save_base(self.object, using=using, raw=True, **kwargs)
  File "/root/.local/lib/python3.11/site-packages/django/db/models/base.py", line 877, in save_base
    updated = self._save_table(
              ^^^^^^^^^^^^^^^^^
  File "/root/.local/lib/python3.11/site-packages/django/db/models/base.py", line 990, in _save_table
    updated = self._do_update(
              ^^^^^^^^^^^^^^^^
  File "/root/.local/lib/python3.11/site-packages/django/db/models/base.py", line 1054, in _do_update
    return filtered._update(values) > 0
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/lib/python3.11/site-packages/django/db/models/query.py", line 1231, in _update
    return query.get_compiler(self.db).execute_sql(CURSOR)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1984, in execute_sql
    cursor = super().execute_sql(result_type)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1562, in execute_sql
    cursor.execute(sql, params)
  File "/root/.local/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
    return self._execute_with_wrappers(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
    return executor(sql, params, many, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
    with self.db.wrap_database_errors:
  File "/root/.local/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/root/.local/lib/python3.11/site-packages/django/db/backends/utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/lib/python3.11/site-packages/psycopg/cursor.py", line 97, in execute
    raise ex.with_traceback(None)
django.db.utils.IntegrityError: Problem installing fixture '/home/inventree/data/backup/data-2025-11-05-1443.json.data.json': Could not load users.UserProfile(pk=1): duplicate key value violates unique constraint "users_userprofile_user_id_key"
DETAIL:  Key (user_id)=(2) already exists.
ERROR: InvenTree command failed: 'python3 manage.py loaddata '/home/inventree/data/backup/data-2025-11-05-1443.json.data.json' -i --exclude contenttypes --exclude auth.permission --exclude error_report.error --exclude admin.logentry --exclude django_q.schedule --exclude django_q.task --exclude django_q.ormq --exclude exchange.rate --exclude exchange.exchangebackend --exclude common.dataoutput --exclude common.newsfeedentry --exclude common.notificationentry --exclude common.notificationmessage --exclude importer.dataimportsession --exclude importer.dataimportcolumnmap --exclude importer.dataimportrow --exclude auth.group --exclude auth.user'

LastEdit avatar Nov 06 '25 00:11 LastEdit

Importing records should work, as long as the database truly is empty beforehand. Is there any way you can inspect the database to ensure it is actually empty?

SchrodingersGat avatar Nov 09 '25 06:11 SchrodingersGat

@SchrodingersGat I'm getting the same error. I think what is going on is that when django restores the auth users, the signal registered in users/models.py is creating profiles automatically for the restored users, so when these profiles are then restored you get the duplicate key errors. Here is the code snippet in question:

# Signal to create or update user profile when user is saved
@receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
    """Create or update user profile when user is saved."""
    if created:
        UserProfile.objects.create(user=instance)
    instance.profile.save()

I stopped the import-records command after restoring the auth-data but before the database records were imported and I can see in my users_userprofile table there are entries corresponding to each added auth_user:

inventree=# select id,metadata,active,type,user_id from users_userprofile;
 id | metadata | active |   type   | user_id 
----+----------+--------+----------+---------
  1 | {}       | t      | internal |       1
  2 | {}       | t      | internal |       2
  3 | {}       | t      | internal |       3
  4 | {}       | t      | internal |       4
(4 rows)

Probably the easiest fix would be to disable this signal for loaddata by exiting the signal when the kwarg "raw" is set to true. See more here: https://docs.djangoproject.com/en/6.0/topics/db/fixtures/#how-fixtures-are-saved-to-the-database

If you want I can make a PR with these changes, but it might be more complicated if you rely on these signals working when deserializing into the db in other contexts.

bbzylstra avatar Nov 17 '25 19:11 bbzylstra

@bbzylstra thanks for tracking this down.

Probably the easiest fix would be to disable this signal for loaddata by exiting the signal when the kwarg "raw" is set to true.

Note that we have other checks for this also:

https://github.com/inventree/InvenTree/blob/57a2de6ffc6f949ab8882b2063bbd8f55a1da48b/src/backend/InvenTree/InvenTree/ready.py#L13

So you might be able to use this also.

In any case, we would welcome a PR to address this!

SchrodingersGat avatar Nov 17 '25 22:11 SchrodingersGat