Repository: django/django
File: django/contrib/auth/management/__init__.py (Python)

Code before:
        for migration, backward in (plan or [])
        for op in migration.operations
        if isinstance(op, migrations.RenameModel)
        and migration.app_label == app_config.label
    ]

    if not renames:
        return

    for old_name, new_name in renames:
        old_suffix = f"_{old_name.lower()}"
        new_suffix = f"_{new_name.lower()}"

        perms = Permission.objects.using(db).filter(
            content_type__app_label=app_label,
            codename__endswith=old_suffix,
        )

        for perm in perms:
            for action in ("add", "change", "delete", "view"):
                if not perm.codename.startswith(action + "_"):
                    continue

                old_codename = perm.codename
                new_codename = f"{action}{new_suffix}"
                new_name_str = f"Can {action} {camel_case_to_spaces(new_name)}"

                if perm.codename == new_codename and perm.name == new_name_str:
                    continue

                perm.codename = new_codename
                perm.name = new_name_str

                try:
                    with transaction.atomic(using=db):
                        perm.save(update_fields={"codename", "name"}, using=db)
                except IntegrityError as exc:
                    # IntegrityError is most likely caused by duplicate
                    # permissions created in pre-Django 6.0 projects before
                    # permission renames were handled.
                    msg = (
                        "Failed to rename permission %(pk)s "
                        "from '%(old)s' to  '%(new)s' because a permission "
                        "with the target codename already exists. \n"
                        "Please resolve the conflict manually."
                    ) % {
                        "pk": perm.pk,
                        "old": old_codename,
                        "new": perm.codename,
                    }
                    exc.add_note(msg)

Review comment (suggestion):
Can you make a try at getting the `verbose_name_raw` instead?
https://github.com/django/django/pull/20481#discussion_r2655816469

Code after:
    except LookupError:
        return
    if not router.allow_migrate_model(using, Permission):
        return

    db = using or router.db_for_write(Permission)

    app_label = app_config.label

    # Collect (from_model, to_model) pairs
    renames = [
        (op.new_name, op.old_name) if backward else (op.old_name, op.new_name)
        for migration, backward in (plan or [])
        for op in migration.operations
        if isinstance(op, migrations.RenameModel)
        and migration.app_label == app_config.label
    ]

    if not renames:
        return

    planned = []
    conflicts = []

    for old_name, new_name in renames:
        old_suffix = f"_{old_name.lower()}"
        new_suffix = f"_{new_name.lower()}"

        actions, verbose_name_raw = _get_permission_metadata(apps, app_label, new_name)
        perms = Permission.objects.using(db).filter(
            content_type__app_label=app_label,
            codename__in=[f"{action}{old_suffix}" for action in actions],
        )

        for perm in perms:
            for action in actions:
                if not perm.codename.startswith(action + "_"):
                    continue

                old_codename = perm.codename
                new_codename = f"{action}{new_suffix}"
                new_name_str = f"Can {action} {verbose_name_raw}"

                planned.append((perm, old_codename, new_codename, new_name_str))

    existing = {
        p.codename
        for p in Permission.objects.using(db).filter(
            content_type__app_label=app_label,
            codename__in=[new for _, _, new, _ in planned],
        )