首页 > 解决方案 > Django 自定义迁移回滚

问题描述

我有以下自定义 Django 迁移代码:

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),
    ]

向前迁移很好。但是,当我尝试回滚此迁移时,出现以下错误:

...
  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.

我不明白这个错误,因为我在 .remove() 方法中传递了一个 Permission 实例,而不是一个字符串。我尝试过用 .filter() 后跟 .get() 替换 .get() 方法。我还在 .remove() 之前放置了一个断点() 以确保类型匹配,并且 can_upload_data 确实是一种类型,而解释器仍然吐出上述错误,表明它是一个字符串。有任何想法吗?

标签: pythondjango

解决方案


因此,在修整了几个小时之后,我发现出于某种原因,在这种情况下 .remove() 方法需要一个想要删除的权限的 ID,而不是权限的实例。

奇怪,但它奏效了。也许是一个错误?我将把它复制到 Django 的 github 问题上进行检查。

Django 版本 2.2.23。


推荐阅读