-
-
Notifications
You must be signed in to change notification settings - Fork 32.8k
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
base: main
Are you sure you want to change the base?
Conversation
f78fc46
to
08f9cb0
Compare
There was a problem hiding this 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 👍
if not router.allow_migrate_model(db, Permission): | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing tests
There was a problem hiding this comment.
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 👍
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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 🤔
4595197
to
968975d
Compare
tests/auth_tests/test_management.py
Outdated
def tearDown(self): | ||
pre_migrate.disconnect(update_permissions) | ||
|
||
def test_fake_migration_plan(self): |
There was a problem hiding this 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!
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
59ec5dc
to
453b00b
Compare
There was a problem hiding this 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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 👍
tests/auth_tests/test_management.py
Outdated
interactive=False, | ||
verbosity=0, | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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( |
There was a problem hiding this comment.
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
def inject_permission_rename_operations( | |
def rename_permissions( |
4750ec9
to
40e3fb1
Compare
Thank you for the updates, I pushed minor stylistic tweaks and updated the commit message |
de634f6
to
4335fd6
Compare
795aaa6
to
d2fc87e
Compare
There was a problem hiding this 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
if not router.allow_migrate_model(db, Permission): | ||
return |
There was a problem hiding this comment.
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 👍
e534fb1
to
7ac68eb
Compare
7ac68eb
to
7530d68
Compare
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
main
branch.