Django custom migration rollback

Viewed 359

I have the following custom Django migration code:

from django.conf import settings
from django.contrib.auth.hashers import make_password
from django.core.management.sql import emit_post_migrate_signal
from django.db import migrations


def create_user_groups_permissions(apps, schema_editor):
    emit_post_migrate_signal(verbosity=1, interactive=False, db='default')

    Group = apps.get_model('auth', 'Group')
    Permission = apps.get_model('auth', 'Permission')
    User = apps.get_model('users', 'User')
    Token = apps.get_model('authtoken', 'Token')

    # Create the default superuser for development
    defaults = {
        'is_active': True,
        'is_staff': True,
        'is_superuser': True,
        'email': settings.SERVER_EMAIL,
    }

    admin, _ = User.objects.get_or_create(username='admin', defaults=defaults)
    
    if settings.ADMIN_PASSWORD_DEV:
        admin.password = make_password(settings.ADMIN_PASSWORD_DEV)
        admin.save()
    
    # Create DRF token for admin
    _, created = Token.objects.get_or_create(user=admin)

    # Get permissions
    can_upload_data = Permission.objects.get(codename='can_upload_data')

    # Create groups with permissions
    default_user, created = Group.objects.get_or_create(name='default_user')
    if created:
        default_user.permissions.add(can_upload_data)

def remove_user_groups_permissions(apps, schema_editor):
    emit_post_migrate_signal(verbosity=1, interactive=False, db='default')

    Group = apps.get_model('auth', 'Group')
    Permission = apps.get_model('auth', 'Permission')
    User = apps.get_model('users', 'User')
    Token = apps.get_model('authtoken', 'Token')

    # Delete DRF token for default superuser
    Token.objects.filter(user__username='admin').delete()

    # Get permissions
    can_upload_data = Permission.objects.get(codename='can_upload_data')

    # Delete permissions from groups
    default_user = Group.objects.get(name='default_user')
    default_user.permissions.remove(can_upload_data)


class Migration(migrations.Migration):

    dependencies = [
        ('users', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(code=create_user_groups_permissions, reverse_code=remove_user_groups_permissions),
    ]

Forward migration is fine with it. However, when I try to rollback this migration I get the following error:

...
  File "/usr/local/lib/python3.9/site-packages/django/db/migrations/operations/special.py", line 196, in database_backwards
    self.reverse_code(from_state.apps, schema_editor)
  File "/the_project_name/main_site/users/migrations/0002_fixtures_groups_permissions.py", line 92, in remove_user_groups_permissions
    default_user.permissions.remove(can_upload_data)
  File "/usr/local/lib/python3.9/site-packages/django/db/models/fields/related_descriptors.py", line 950, in remove
    self._remove_items(self.source_field_name, self.target_field_name, *objs)
  File "/usr/local/lib/python3.9/site-packages/django/db/models/fields/related_descriptors.py", line 1129, in _remove_items
    self.through._default_manager.using(db).filter(filters).delete()
  File "/usr/local/lib/python3.9/site-packages/django/db/models/query.py", line 892, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/django/db/models/query.py", line 910, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "/usr/local/lib/python3.9/site-packages/django/db/models/sql/query.py", line 1290, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "/usr/local/lib/python3.9/site-packages/django/db/models/sql/query.py", line 1310, in _add_q
    child_clause, needed_inner = self._add_q(
  File "/usr/local/lib/python3.9/site-packages/django/db/models/sql/query.py", line 1315, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "/usr/local/lib/python3.9/site-packages/django/db/models/sql/query.py", line 1224, in build_filter
    self.check_related_objects(join_info.final_field, value, join_info.opts)
  File "/usr/local/lib/python3.9/site-packages/django/db/models/sql/query.py", line 1087, in check_related_objects
    self.check_query_object_type(v, opts, field)
  File "/usr/local/lib/python3.9/site-packages/django/db/models/sql/query.py", line 1065, in check_query_object_type
    raise ValueError(
ValueError: Cannot query "Permission object (51)": Must be "Permission" instance.

I don`t understand this error, since I am passing a Permission instance in the .remove() method there, not a string. I have tried experimenting with replacing .get() method with .filter() followed by .get(). I have also placed a breakpoint() right before .remove() to make sure types matched up, and can_upload_data is indeed a type, while interpretter still spits out the above error suggesting it is a String. Any ideas?

1 Answers

So after tinkering for a good few hours, I found out that for some reason .remove() method in this case expects an ID of the permission one would like to remove, instead of the Permission's instance.

Strange, but it worked. Maybe a bug? I`ll copy this over to Django's github issues to have it checked.

Django version 2.2.23.

Related