Repository: saleor/saleor

Issue:
Cannot migrate onto '0041_permissions_to_groups'
### What I'm trying to achieve
Migrate to latest saleor.

### Steps to reproduce the problem
1. (probably) try to checkout to an older version of saleor
1. populate db
1. migrate to latest
1. migrate 

### What I expected to happen
```
Running migrations:
Applying account.0041_permissions_to_groups...Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
psycopg2.errors.StringDataRightTruncation: value too long for type character varying(150)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 395, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 328, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 369, in execute
output = self.handle(*args, **options)
File "/usr/local/lib/python3.8/site-packages/django/core/management/base.py", line 83, in wrapped
res = handle_func(*args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/django/core/management/commands/migrate.py", line 231, in handle
post_migrate_state = executor.migrate(
File "/usr/local/lib/python3.8/site-packages/django/db/migrations/executor.py", line 117, in migrate
state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)
File "/usr/local/lib/python3.8/site-packages/django/db/migrations/executor.py", line 147, in _migrate_all_forwards
state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
File "/usr/local/lib/python3.8/site-packages/django/db/migrations/executor.py", line 245, in apply_migration
state = migration.apply(state, schema_editor)
File "/usr/local/lib/python3.8/site-packages/django/db/migrations/migration.py", line 124, in apply
operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
File "/usr/local/lib/python3.8/site-packages/django/db/migrations/operations/special.py", line 190, in database_forwards
self.code(from_state.apps, schema_editor)
File "/app/saleor/account/migrations/0041_permissions_to_groups.py", line 26, in add_users_to_groups_based_on_users_permissions
group = create_group_with_given_permissions(perms, group_data.group_name, Group)
File "/app/saleor/account/migrations/0041_permissions_to_groups.py", line 65, in create_group_with_given_permissions
group = Group.objects.create(name=group_name)
File "/usr/local/lib/python3.8/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 433, in create
obj.save(force_insert=True, using=self.db)
File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 745, in save
self.save_base(using=using, force_insert=force_insert,
File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 782, in save_base
updated = self._save_table(
File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 887, in _save_table
results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 924, in _do_insert
return manager._insert(
File "/usr/local/lib/python3.8/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 1204, in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1391, in execute_sql
cursor.execute(sql, params)
File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 68, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
File "/usr/local/lib/python3.8/site-packages/django/db/utils.py", line 90, in __exit__
File "/usr/local/lib/python3.8/site-packages/django/db/migrations/executor.py", line 245, in apply_migration
state = migration.apply(state, schema_editor)
File "/usr/local/lib/python3.8/site-packages/django/db/migrations/migration.py", line 124, in apply
operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
File "/usr/local/lib/python3.8/site-packages/django/db/migrations/operations/special.py", line 190, in database_forwards
self.code(from_state.apps, schema_editor)
File "/app/saleor/account/migrations/0041_permissions_to_groups.py", line 26, in add_users_to_groups_based_on_users_permissions
group = create_group_with_given_permissions(perms, group_data.group_name, Group)
File "/app/saleor/account/migrations/0041_permissions_to_groups.py", line 65, in create_group_with_given_permissions
group = Group.objects.create(name=group_name)
File "/usr/local/lib/python3.8/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 433, in create
obj.save(force_insert=True, using=self.db)
File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 745, in save
self.save_base(using=using, force_insert=force_insert,
File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 782, in save_base
updated = self._save_table(
File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 887, in _save_table
results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 924, in _do_insert
return manager._insert(
File "/usr/local/lib/python3.8/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 1204, in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1391, in execute_sql
cursor.execute(sql, params)
File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 68, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
File "/usr/local/lib/python3.8/site-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
django.db.utils.DataError: value too long for type character varying(150)
```

### Screenshots
<!-- If applicable, add screenshots to help explain your problem. -->

**System information**
Operating system:



Discussion:

Resolving patch:
diff --git a/saleor/account/migrations/0041_permissions_to_groups.py b/saleor/account/migrations/0041_permissions_to_groups.py
--- a/saleor/account/migrations/0041_permissions_to_groups.py
+++ b/saleor/account/migrations/0041_permissions_to_groups.py
@@ -1,4 +1,5 @@
-from collections import namedtuple
+import re
+from collections import defaultdict
 
 from django.db import migrations
 
@@ -12,46 +13,45 @@ def add_users_to_groups_based_on_users_permissions(apps, schema_editor):
     """
     User = apps.get_model("account", "User")
     Group = apps.get_model("auth", "Group")
-    GroupData = namedtuple("GroupData", ["users", "group_name"])
 
     groups = Group.objects.all().prefetch_related("permissions")
-
-    mapping = create_permissions_mapping(User, GroupData)
-    for perms, group_data in mapping.items():
+    counter = get_counter_value(Group)
+    mapping = create_permissions_mapping(User)
+    for perms, users in mapping.items():
         group = get_group_with_given_permissions(perms, groups)
-        users = group_data.users
         if group:
             group.user_set.add(*users)
             continue
-        group = create_group_with_given_permissions(perms, group_data.group_name, Group)
+        group = create_group_with_given_permissions(perms, counter, Group)
         group.user_set.add(*users)
+        counter += 1
+
+
+def get_counter_value(Group):
+    """Get the number of next potential group."""
+    pattern = r"^Group (\d+)$"
+    group = Group.objects.filter(name__iregex=pattern).order_by("name").last()
+    if not group:
+        return 1
+    return int(re.match(pattern, group.name).group(1)) + 1
 
 
-def create_permissions_mapping(User, GroupData):
+def create_permissions_mapping(User):
     """Create mapping permissions to users and potential new group name."""
-    mapping = {}
-    users = User.objects.filter(user_permissions__isnull=False).prefetch_related(
-        "user_permissions"
+    mapping = defaultdict(set)
+    users = (
+        User.objects.filter(user_permissions__isnull=False)
+        .distinct()
+        .prefetch_related("user_permissions")
     )
     for user in users:
-        permissions = user.user_permissions.all()
-        perm_pks = (perm.pk for perm in permissions)
-        if perm_pks not in mapping:
-            group_name = create_group_name(permissions)
-            mapping[perm_pks] = GroupData({user.pk}, group_name)
-        else:
-            mapping[perm_pks].users.add(user.pk)
+        permissions = user.user_permissions.all().order_by("pk")
+        perm_pks = tuple([perm.pk for perm in permissions])
+        mapping[perm_pks].add(user.pk)
         user.user_permissions.clear()
     return mapping
 
 
-def create_group_name(permissions):
-    """Create group name based on permissions."""
-    formatted_names = [perm.name.rstrip(".").lower() for perm in permissions]
-    group_name = ", ".join(formatted_names).capitalize()
-    return group_name
-
-
 def get_group_with_given_permissions(permissions, groups):
     """Get group with given set of permissions."""
     for group in groups:
@@ -60,8 +60,9 @@ def get_group_with_given_permissions(permissions, groups):
             return group
 
 
-def create_group_with_given_permissions(perm_pks, group_name, Group):
+def create_group_with_given_permissions(perm_pks, counter, Group):
     """Create new group with given set of permissions."""
+    group_name = f"Group {counter:03d}"
     group = Group.objects.create(name=group_name)
     group.permissions.add(*perm_pks)
     return group


Test patch: