Skip to content

Fixed #27489 -- Renamed permissions upon model renaming. #19405

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

artirix1927
Copy link

@artirix1927 artirix1927 commented Apr 20, 2025

ticket-27489

Refs#24067
By looking at a code that was used to fix Refs#24067 I added an update permissions function that gets called pre migrate using signals. It looks for rename model operations inside the migrate plan and adds new custom operation to the plan that will change the permission codename and name.

Added regression test that creates permission for fake model, creates fake migration for the model rename and calls the update permission function using signals and manually does 'state_forward' to apply.

Then check for the changes

Checklist

  • This PR targets the main branch.
  • The commit message is written in past tense, mentions the ticket number, and ends with a period.
  • I have checked the "Has patch" ticket flag in the Trac system.
  • I have added or updated relevant tests.
  • I have added or updated relevant docs, including release notes if applicable.
  • I have attached screenshots in both light and dark modes for any UI changes.

@artirix1927 artirix1927 force-pushed the ticket_27489 branch 2 times, most recently from f78fc46 to 08f9cb0 Compare April 20, 2025 22:48
Copy link
Contributor

@sarahboyce sarahboyce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR ⭐
I have checked the test coverage and we still a few more tests here 👍

Comment on lines 127 to 130
if not router.allow_migrate_model(db, Permission):
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing tests

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry this is still missing a test 👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify — for the missing test, do you mean I should assert that the RenamePermission operation is not in the migration plan when the router disallows it? I want to make sure I understand what you expecting from the test. Sorry for being a little bit slow

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case, the RenamePermission is in the plan but router.allow_migrate_model(db, Permission) returns False and so no permission renames, or queries, should take place

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it would be appropriate to call the RenamePermission manually in this specific case? Because I cant find a way to write the test so the router allows migration at the stage of building the migration plan (so it can actually insert the rename operation) but disallows it in the runtime (when the actual rename happens).

       op = RenamePermission(
            app_label="auth_tests",
            old_model="OldModel",
            new_model="NewModel",
        )

        with connection.schema_editor() as schema_editor:
            op.rename_forward(apps, schema_editor)
 

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we just remove it 🤔
We have copied this because it exists in django.contrib.contenttypes.management.RenameContentType._rename but this is also not covered by tests and may not be needed 🤔

@artirix1927 artirix1927 force-pushed the ticket_27489 branch 3 times, most recently from 4595197 to 968975d Compare May 31, 2025 04:59
def tearDown(self):
pre_migrate.disconnect(update_permissions)

def test_fake_migration_plan(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the updates!

I just realized we might want the testing to be closer to what was done in f179113 where we call migrate and migrate back to zero to check everything was undone. I think this is cleaner than having some of the "setup" and "clean state" that exists in some of the tests. Would you mind taking a look?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@artirix1927 artirix1927 force-pushed the ticket_27489 branch 2 times, most recently from 59ec5dc to 453b00b Compare June 30, 2025 05:55
@sarahboyce sarahboyce changed the title Fixed #27489 permission update on model rename Fixed #24067 -- Renamed permissions upon model renaming. Jul 18, 2025
Copy link
Contributor

@sarahboyce sarahboyce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the updates! We're almost there, have a few final questions


def _rename(self, apps, schema_editor, old_model, new_model):
ContentType = apps.get_model("contenttypes", "ContentType")
from django.contrib.auth.models import Permission # real live model import
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why this shouldn't be Permission = apps.get_model("auth", "Permission")?
I see there are some test failures but the direct import feels wrong

Copy link
Author

@artirix1927 artirix1927 Jul 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so as I understand, the tests don't pass because the apps.get_model() function returns frozen (__fake__) model. Which does not have foreign key relationships yet and does not have content_type field at all. I tried to fix this by avoiding the foreign keys and content types to find the needed permissions, but the permission class has its own ordering that involves contenttypes so this does not work too. I can call .order_by() inside the operation, which will clear the ordering and wont raise errors and will allow me to query for permissions by codename strings and etc, while avoiding contettypes. Other than that, the he only way I could do this is by using raw sql inside the migration operation which seems even worse than using live model.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the best option we have 👍

interactive=False,
verbosity=0,
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.assertFalse(Permission.objects.filter(codename="change_newmodel").exists())

When I add the following assertion to check this has reversed, this fails. Can you look into this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into this and to be honest that test is not needed and it didnt pass the test because when creating a contenttype for this test I put the "model=" with camel case which wont ever happen in real app since django manages the names by itself and lowers all the names of the models to lower case automatically.


def _rename(self, apps, schema_editor, old_model, new_model):
ContentType = apps.get_model("contenttypes", "ContentType")
from django.contrib.auth.models import Permission # real live model import
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the best option we have 👍

for permission in permissions:
prefix = permission.codename.split("_")[0]
new_codename = f"{prefix}_{new_model}"
new_name = f"Can {prefix} {new_model}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
new_name = f"Can {prefix} {new_model}"
default_verbose_name = camel_case_to_spaces(new_model)
new_name = f"Can {prefix} {default_verbose_name}"

I think the Permission name uses the model verbose_name which I'm not sure we can access but we can probably do a better job of building. This will need some test updates 👍

self._rename(apps, schema_editor, self.new_model, self.old_model)


def inject_permission_rename_operations(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not to keep the same naming pattern? inject_*_operations sounds unnecessary

Suggested change
def inject_permission_rename_operations(
def rename_permissions(

@artirix1927 artirix1927 force-pushed the ticket_27489 branch 3 times, most recently from 4750ec9 to 40e3fb1 Compare July 23, 2025 06:53
@sarahboyce
Copy link
Contributor

Thank you for the updates, I pushed minor stylistic tweaks and updated the commit message
The tests currently fail when ran in reverse (./runtests.py auth_tests.test_management.PermissionRenameOperationsTests --reverse), can you take a look?

@artirix1927 artirix1927 changed the title Fixed #24067 -- Renamed permissions upon model renaming. Fixed #27489 -- Renamed permissions upon model renaming. Jul 31, 2025
@artirix1927 artirix1927 force-pushed the ticket_27489 branch 2 times, most recently from de634f6 to 4335fd6 Compare August 1, 2025 04:39
@sarahboyce sarahboyce force-pushed the ticket_27489 branch 3 times, most recently from 795aaa6 to d2fc87e Compare August 4, 2025 15:55
Copy link
Contributor

@sarahboyce sarahboyce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, pushed minor changes

Comment on lines 127 to 130
if not router.allow_migrate_model(db, Permission):
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry this is still missing a test 👍

@artirix1927 artirix1927 force-pushed the ticket_27489 branch 2 times, most recently from e534fb1 to 7ac68eb Compare August 10, 2025 07:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants