Repository: WeblateOrg/weblate

Issue:
Update problem (Handled exception: MultipleObjectsReturned)
### Describe the issue

Obecna wersja:
<html>
<body>
<!--StartFragment-->
5.4-dev — 64065e6bbf126a6461f99bcaa6ac63648d0198ab
--


<!--EndFragment-->
</body>
</html>5.4-dev — [64065e6bbf126a6461f99bcaa6ac63648d0198ab](https://github.com/WeblateOrg/weblate/commits/64065e6bbf126a6461f99bcaa6ac63648d0198ab)

An error occurs when updating to the latest bleeding version. The problem has been present for at least a week (that's when I tried updating for the first time).
I am attaching the logs:


```
weblate@weblate:~/weblate-docker$ sudo docker-compose -f docker-compose-https.yml -f docker-compose-https.override.yml up -d
[+] Running 5/5
 ✔ Network weblate-docker_default           Created                                                                                                                                                                                  0.3s
 ✔ Container weblate-docker-https-portal-1  Started                                                                                                                                                                                 20.2s
 ✔ Container weblate-docker-cache-1         Started                                                                                                                                                                                 20.2s
 ✔ Container weblate-docker-database-1      Started                                                                                                                                                                                 20.2s
 ✔ Container weblate-docker-weblate-1       Started                                                                                                                                                                                  0.1s
weblate@weblate:~/weblate-docker$ sudo docker compose logs -f
weblate-1   | Starting Weblate 5.4+2024-02-17...
cache-1     | 1:C 17 Feb 2024 13:22:04.640 # WARNING Your system is configured to use the 'xen' clocksource which might lead to degraded performance. Check the result of the [slow-clocksource] system check: run 'redis-server --check-system' to check if the system's clocksource isn't degrading performance.
cache-1     | 1:C 17 Feb 2024 13:22:04.640 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
cache-1     | 1:C 17 Feb 2024 13:22:04.640 * Redis version=7.2.4, bits=64, commit=00000000, modified=0, pid=1, just started
database-1  |
database-1  | PostgreSQL Database directory appears to contain a database; Skipping initialization
database-1  |
database-1  | 2024-02-17 13:22:05.088 UTC [1] LOG:  starting PostgreSQL 16.2 on x86_64-pc-linux-musl, compiled by gcc (Alpine 13.2.1_git20231014) 13.2.1 20231014, 64-bit
database-1  | 2024-02-17 13:22:05.088 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
database-1  | 2024-02-17 13:22:05.088 UTC [1] LOG:  listening on IPv6 address "::", port 5432
database-1  | 2024-02-17 13:22:05.093 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
database-1  | 2024-02-17 13:22:05.100 UTC [25] LOG:  database system was shut down at 2024-02-17 13:19:30 UTC
database-1  | 2024-02-17 13:22:05.112 UTC [1] LOG:  database system is ready to accept connections
cache-1     | 1:C 17 Feb 2024 13:22:04.640 * Configuration loaded
cache-1     | 1:M 17 Feb 2024 13:22:04.641 * monotonic clock: POSIX clock_gettime
cache-1     | 1:M 17 Feb 2024 13:22:04.642 * Running mode=standalone, port=6379.
cache-1     | 1:M 17 Feb 2024 13:22:04.644 * Server initialized
cache-1     | 1:M 17 Feb 2024 13:22:04.645 * Loading RDB produced by version 7.2.4
cache-1     | 1:M 17 Feb 2024 13:22:04.645 * RDB age 154 seconds
cache-1     | 1:M 17 Feb 2024 13:22:04.645 * RDB memory usage when created 35.70 Mb
cache-1     | 1:M 17 Feb 2024 13:22:05.212 * Done loading RDB, keys loaded: 166796, keys expired: 35.
cache-1     | 1:M 17 Feb 2024 13:22:05.212 * DB loaded from disk: 0.567 seconds
cache-1     | 1:M 17 Feb 2024 13:22:05.212 * Ready to accept connections tcp
weblate-1   | System check identified no issues (1 silenced).
weblate-1   | Postgres 160002 is up
weblate-1   | Starting database migration...
weblate-1   | Operations to perform:
weblate-1   |   Apply all migrations: accounts, addons, admin, auth, authtoken, checks, configuration, contenttypes, django_celery_beat, fonts, gitexport, glossary, lang, memory, metrics, screenshots, sessions, social_django, trans, utils, vcs, weblate_auth, wladmin
weblate-1   | Running migrations:
weblate-1   |   Applying memory.0002_memory_memory_md5_index... OK
weblate-1   |   Applying memory.0003_drop_hash_index... OK
weblate-1   |   Applying trans.0012_alter_announcement_notify... OK
weblate-1   |   Applying weblate_auth.0003_alter_autogroup_options_alter_autogroup_group_and_more... OK
weblate-1   | [2024-02-17 14:22:52,317: WARNING/72] Handled exception: MultipleObjectsReturned: get() returned more than one Group -- it returned 2!
weblate-1   | Traceback (most recent call last):
weblate-1   |   File "/usr/local/bin/weblate", line 8, in <module>
weblate-1   |     sys.exit(main())
weblate-1   |              ^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/weblate/runner.py", line 19, in main
weblate-1   |     utility.execute()
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 436, in execute
weblate-1   |     self.fetch_command(subcommand).run_from_argv(self.argv)
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 412, in run_from_argv
weblate-1   |     self.execute(*args, **cmd_options)
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 458, in execute
weblate-1   |     output = self.handle(*args, **options)
weblate-1   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 106, in wrapper
weblate-1   |     res = handle_func(*args, **kwargs)
weblate-1   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/commands/migrate.py", line 383, in handle
weblate-1   |     emit_post_migrate_signal(
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/sql.py", line 52, in emit_post_migrate_signal
weblate-1   |     models.signals.post_migrate.send(
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/dispatch/dispatcher.py", line 177, in send
weblate-1   |     (receiver, receiver(signal=self, sender=sender, **named))
weblate-1   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/django/signals_handlers.py", line 76, in wrapper
weblate-1   |     return receiver(*args, **kwargs)
weblate-1   |            ^^^^^^^^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/weblate/auth/models.py", line 851, in sync_create_groups
weblate-1   |     create_groups(False)
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/weblate/auth/models.py", line 820, in create_groups
weblate-1   |     migrate_groups(Group, Role, update)
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/weblate/auth/utils.py", line 77, in migrate_groups
weblate-1   |     instance, created = model.objects.get_or_create(
weblate-1   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/db/models/manager.py", line 87, in manager_method
weblate-1   |     return getattr(self.get_queryset(), name)(*args, **kwargs)
weblate-1   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/db/models/query.py", line 916, in get_or_create
weblate-1   |     return self.get(**kwargs), False
weblate-1   |            ^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/db/models/query.py", line 640, in get
weblate-1   |     raise self.model.MultipleObjectsReturned(
weblate-1   | weblate.auth.models.Group.MultipleObjectsReturned: get() returned more than one Group -- it returned 2!
weblate-1   | Sentry is attempting to send 2 pending events
weblate-1   | Waiting up to 2 seconds
weblate-1   | Press Ctrl-C to quit
weblate-1   |
weblate-1   | Database migration has failed. Please check the error message above.
weblate-1   | Note: Upgrading across major versions is not supported. In case you are upgrading
weblate-1   |       from an 4.x version, please upgrade to 5.0.2 first.
weblate-1   |
weblate-1   |       Using following for the docker-compose.yaml:
weblate-1   |
weblate-1   |           image: weblate/weblate:5.0.2.2
```



### I already tried

- [X] I've read and searched [the documentation](https://docs.weblate.org/).
- [X] I've searched for similar filed issues in this repository.

### Steps to reproduce the behavior

1 docker compose stop
2 docker compose pull
3 docker-compose -f docker-compose-https.yml -f docker-compose-https.override.yml up -d

### Expected behavior

_No response_

### Screenshots

_No response_

### Exception traceback

_No response_

### How do you run Weblate?

Docker container

### Weblate versions

_No response_

### Weblate deploy checks

_No response_

### Additional context

_No response_


Discussion:
Thank you for your report; the issue you have reported has just been fixed.

* In case you see a problem with the fix, please comment on this issue.
* In case you see a similar problem, please open a separate issue.
* If you are happy with the outcome, don’t hesitate to support Weblate by making a [donation](https://weblate.org/donate/).
@nijel 
```

weblate-1   | Starting database migration...
weblate-1   | Operations to perform:
weblate-1   |   Apply all migrations: accounts, addons, admin, auth, authtoken, checks, configuration, contenttypes, django_celery_beat, fonts, gitexport, glossary, lang, memory, metrics, screenshots, sessions, social_django, trans, utils, vcs, weblate_auth, wladmin
weblate-1   | Running migrations:
weblate-1   |   Applying memory.0002_memory_memory_md5_index... OK
weblate-1   |   Applying memory.0003_drop_hash_index... OK
weblate-1   |   Applying trans.0012_alter_announcement_notify... OK
weblate-1   |   Applying weblate_auth.0003_fixup_teams...Found duplicate team<166>: 'Guests', renaming to 'Guests (2)'
weblate-1   | Found duplicate team<167>: 'Viewers', renaming to 'Viewers (2)'
weblate-1   |  OK
weblate-1   |   Applying weblate_auth.0003_alter_autogroup_options_alter_autogroup_group_and_more... OK
weblate-1   |   Applying weblate_auth.0004_merge_20240219_0936... OK
weblate-1   | [2024-02-19 22:44:52,185: WARNING/72] Handled exception: DoesNotExist: Group matching query does not exist.
weblate-1   | Traceback (most recent call last):
weblate-1   |   File "/usr/local/bin/weblate", line 8, in <module>
weblate-1   |     sys.exit(main())
weblate-1   |              ^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/weblate/runner.py", line 19, in main
weblate-1   |     utility.execute()
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 436, in execute
weblate-1   |     self.fetch_command(subcommand).run_from_argv(self.argv)
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 412, in run_from_argv
weblate-1   |     self.execute(*args, **cmd_options)
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 458, in execute
weblate-1   |     output = self.handle(*args, **options)
weblate-1   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 106, in wrapper
weblate-1   |     res = handle_func(*args, **kwargs)
weblate-1   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/commands/migrate.py", line 383, in handle
weblate-1   |     emit_post_migrate_signal(
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/core/management/sql.py", line 52, in emit_post_migrate_signal
weblate-1   |     models.signals.post_migrate.send(
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/dispatch/dispatcher.py", line 177, in send
weblate-1   |     (receiver, receiver(signal=self, sender=sender, **named))
weblate-1   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/django/signals_handlers.py", line 76, in wrapper
weblate-1   |     return receiver(*args, **kwargs)
weblate-1   |            ^^^^^^^^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/weblate/auth/models.py", line 851, in sync_create_groups
weblate-1   |     create_groups(False)
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/weblate/auth/models.py", line 834, in create_groups
weblate-1   |     group = Group.objects.get(
weblate-1   |             ^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/db/models/manager.py", line 87, in manager_method
weblate-1   |     return getattr(self.get_queryset(), name)(*args, **kwargs)
weblate-1   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
weblate-1   |   File "/usr/local/lib/python3.12/site-packages/django/db/models/query.py", line 637, in get
weblate-1   |     raise self.model.DoesNotExist(
weblate-1   | weblate.auth.models.Group.DoesNotExist: Group matching query does not exist.
weblate-1   | Sentry is attempting to send 2 pending events
weblate-1   | Waiting up to 2 seconds
weblate-1   | Press Ctrl-C to quit
weblate-1   |
weblate-1   | Database migration has failed. Please check the error message above.
weblate-1   | Note: Upgrading across major versions is not supported. In case you are upgrading
weblate-1   |       from an 4.x version, please upgrade to 5.0.2 first.
weblate-1   |
weblate-1   |       Using following for the docker-compose.yaml:
weblate-1   |
weblate-1   |           image: weblate/weblate:5.0.2.2
weblate-1 exited with code 0

```

Resolving patch:
diff --git a/weblate/auth/models.py b/weblate/auth/models.py
--- a/weblate/auth/models.py
+++ b/weblate/auth/models.py
@@ -817,26 +817,16 @@ def create_groups(update):
     # Create permissions and roles
     migrate_permissions(Permission)
     new_roles = migrate_roles(Role, Permission)
-    migrate_groups(Group, Role, update)
+    builtin_groups = migrate_groups(Group, Role, update)
 
     # Create anonymous user
     create_anonymous(User, Group, update)
 
     # Automatic assignment to the users group
-    group = Group.objects.get(
-        name="Users",
-        internal=True,
-        project_selection=SELECTION_ALL_PUBLIC,
-        language_selection=SELECTION_ALL,
-    )
+    group = builtin_groups["Users"]
     if not AutoGroup.objects.filter(group=group).exists():
         AutoGroup.objects.create(group=group, match="^.*$")
-    group = Group.objects.get(
-        name="Viewers",
-        internal=True,
-        project_selection=SELECTION_ALL_PROTECTED,
-        language_selection=SELECTION_ALL,
-    )
+    group = builtin_groups["Viewers"]
     if not AutoGroup.objects.filter(group=group).exists():
         AutoGroup.objects.create(group=group, match="^.*$")
 
diff --git a/weblate/auth/utils.py b/weblate/auth/utils.py
--- a/weblate/auth/utils.py
+++ b/weblate/auth/utils.py
@@ -5,6 +5,7 @@
 from __future__ import annotations
 
 from email.headerregistry import Address
+from typing import TYPE_CHECKING
 
 from django.conf import settings
 from django.contrib.auth.hashers import make_password
@@ -18,6 +19,9 @@
     SELECTION_ALL,
 )
 
+if TYPE_CHECKING:
+    from weblate.auth.models import Group, Role
+
 
 def is_django_permission(permission: str):
     """
@@ -71,8 +75,11 @@ def migrate_roles(model, perm_model) -> set[str]:
     return result
 
 
-def migrate_groups(model, role_model, update=False):
+def migrate_groups(
+    model: type[Group], role_model: type[Role], update: bool = False
+) -> dict[str, Group]:
     """Create groups as defined in the data."""
+    result: dict[str, Group] = {}
     for group, roles, selection in GROUPS:
         instance, created = model.objects.get_or_create(
             name=group,
@@ -83,12 +90,14 @@ def migrate_groups(model, role_model, update=False):
                 "language_selection": SELECTION_ALL,
             },
         )
+        result[group] = instance
         if update and not created:
             instance.project_selection = selection
             instance.language_selection = SELECTION_ALL
             instance.save()
         if created or update:
             instance.roles.set(role_model.objects.filter(name__in=roles), clear=True)
+    return result
 
 
 def create_anonymous(model, group_model, update=True):


Test patch: