TIL - You can’t set permissions in Django migrations
Setting permissions in a datamigration does not work.
The code below will not apply any migration to the selected group:
This is really annoying as, despite my years of experience with Django, I keep forgetting this, and redescovering it after a lot of head desks
But why does this happens?
ContentType table (and permissions which relies on content types to work) is only created after the first pass of apply migrations have been run; if you try to fetch permissions objects from database before that, you’ll get nothing:
Permission.objects.filter() will always be empty.
This is especially tricky if you create the datamigration after you have already applied the migrations: in this case the datamigration will work just fine, as
Permission objects will be there in the database.
But as soon as a colleague of yours setup the project starting from an empty database … 💥 … no default permissions.
For more details about this behavior, check this ticket.
How to solve this?
There are two options: creating a django command, or creating the permission in the data migration itseld
Using a django command
Just move the logic from the data migration function to a django command:
This is easy, but it’s something you have to run yourself and it will not be automated by Django.
Creating the permissions in the django migration
While the permissions are not there, you can still create them in your own datamigration: this will not conflict with the automatic creation by Django, as the ones create by your datamigration will be detected and they will be left intact.
The added lines will ensure that all permissions are created by looping over existing applications and creating the applications.
The best part of this approach is that you can create a test to ensure permission are assigned when the migrations are applied.