【问题标题】:Remove old permissions in django删除 django 中的旧权限
【发布时间】:2018-04-21 06:37:49
【问题描述】:

在我的 Django 站点中,有一些权限条目链接到我已删除的应用程序。例如,我有链接到“仪表板”和“Jet”应用程序的权限条目。如何删除它们?

【问题讨论】:

  • 编写迁移,查询 Permission 对象并调用 delete。

标签: django


【解决方案1】:

如果您希望删除自定义或基于模型的(默认)权限,您可以编写如下命令来完成此任务:

from django.conf import settings
from django.contrib.auth.models import Permission
from django.core.management.base import BaseCommand
import django.apps

class Command(BaseCommand):
    help = 'Remove custom permissions that are no longer in models'

    def handle(self, *args, **options):
        # get the db name needed for removal...
        database_name = input('Database Name: ')

        default_perm_names = list()
        # are real perms in db, may not be accurate
        db_custom_perm_names = list()
        # will be used to ensure they are correct.
        meta_custom_perm_names = list()

        default_and_custom_perms = list()

        for model in django.apps.apps.get_models():
            # add to models found to fix perms from removed models
            app_label = model._meta.app_label
            lower_model_name = model._meta.model_name

            all_model_permissions = Permission.objects.using(database_name).filter(content_type__app_label=app_label, content_type__model=lower_model_name)


            default_and_custom_perms.extend([x for x in all_model_permissions])
            # get the custom meta permissions, these should be in the meta of the class
            # will be a list or tuple or list, [0=codename, 1=name]
            meta_permissions = model._meta.permissions

            if meta_permissions:
                for perm in all_model_permissions:
                    # will be the model name from the content type, this is how django makes default perms
                    # we are trying to remove them so now we can figure out which ones are default by provided name
                    model_name_lower = perm.content_type.name
                    # default_perms =  ['add', 'change', 'view', 'delete', 'undelete']
                    # append them to the list of default names
                    default_perm_names.append(f'Can add {model_name_lower}')
                    default_perm_names.append(f'Can change {model_name_lower}')
                    default_perm_names.append(f'Can view {model_name_lower}')
                    default_perm_names.append(f'Can delete {model_name_lower}')
                    default_perm_names.append(f'Can undelete {model_name_lower}')
                    # will mean this is a custom perm...so add it
                    if not perm.name in default_perm_names:
                        db_custom_perm_names.append(perm.name)

                # the perms to ensure are correct...
                for model_perm in meta_permissions:
                    # get the meta perm, will be a list or tuple or list, [0=codename, 1=name]
                    custom_perm = Permission.objects.using(database_name).get(codename=model_perm[0], name=model_perm[1])
                    meta_custom_perm_names.append(custom_perm.name)


        perms_to_remove = [perm for perm in db_custom_perm_names if perm not in meta_custom_perm_names]
        if not perms_to_remove:
            print('There are no stale custom permissions to remove.')


        # print(perms_to_remove)
        # now remove the custom permissions that were removed from the model
        for actual_permission_to_remove in Permission.objects.using(database_name).filter(name__in=perms_to_remove):
            # print(actual_permission_to_remove)
            actual_permission_to_remove.delete(using=database_name)
            print(actual_permission_to_remove, '...deleted')

        for perm in [x for x in Permission.objects.using(database_name)]:
            # loop all perms...if it is not in the model perms it does not exist...
            if perm.content_type.model not in [x.content_type.model for x in default_and_custom_perms]:
                perm.delete(using=database_name)
                print(perm, 'regular permission...deleted')

如果您将命令文件命名为fix_permissions.py,则通过python manage.py fix_permissions 调用

【讨论】:

  • 我喜欢这个解决方案,因为它允许您随时清理权限。如果您从项目开始应用它,则可接受的解决方案是可以的。已接受的解决方案的另一个缺点是,您不能忘记添加迁移脚本,也不要在其中出错……此解决方案也比 Django 提供的解决方案更加用户友好:remove_stale_contenttypes。跨度>
【解决方案2】:

权限在后台具有内容类型的外键,因此删除不再存在的模型的内容类型也会删除这些模型的权限。

幸运的是,Django 还提供了一个manage.py 命令来删除旧的内容类型:remove_stale_contenttypes。运行该命令将列出不再存在的内容类型以及将被删除的相关对象(包括权限),以便您查看更改并批准它们。

$ manage.py remove_stale_contenttypes
Some content types in your database are stale and can be deleted.
Any objects that depend on these content types will also be deleted.
The content types and dependent objects that would be deleted are:

    - Content type for stale_app.removed_model
    - 4 auth.Permission object(s)

This list doesn't include any cascade deletions to data outside of Django's
models (uncommon).

Are you sure you want to delete these content types?
If you're unsure, answer 'no'.
Type 'yes' to continue, or 'no' to cancel:

【讨论】:

  • 此管理命令还接受一个可选参数--include-stale-apps:“删除过时的内容类型,包括以前安装的应用程序中已从 INSTALLED_APPS 中删除的内容类型。” (引自命令帮助文本)。
【解决方案3】:

我是这样做的:

import re 

for perm in Permission.objects.all():
    if re.match( r".+modelname.+permissionname.+",str(perm)):
        print(perm)
        perm.delete()

【讨论】:

    【解决方案4】:

    首先,创建一个空的迁移文件:

    python manage.py makemigrations --empty yourappname
    

    更改迁移(这是一个示例,根据您的需要进行调整):

    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals    
    from django.db import migrations    
    
    
    def add_permissions(apps, schema_editor):
        pass
    
    
    def remove_permissions(apps, schema_editor):
        """Reverse the above additions of permissions."""
        ContentType = apps.get_model('contenttypes.ContentType')
        Permission = apps.get_model('auth.Permission')
        content_type = ContentType.objects.get(
            model='somemodel',
            app_label='yourappname',
        )
        # This cascades to Group
        Permission.objects.filter(
            content_type=content_type,
            codename__in=('add_somemodel', 'change_somemodel', 'delete_somemodel'),
        ).delete()
    
    class Migration(migrations.Migration):
        dependencies = [
            ('yourappname', '0001_initial'),
        ]
        operations = [
            migrations.RunPython(remove_permissions, add_permissions),
        ]
    

    【讨论】:

    • 注意:如果权限是在之前迁移中创建的自定义权限,您需要在单独的migrate 命令中运行此权限,因为对象仅在 all 之后创建已应用迁移(因此在到达 Permission.objects.filter 时会收到 DoesNotExist 错误)
    • 我尝试了上述解决方案来删除默认模型权限,但没有成功。我目前拥有默认和自定义创建的权限。我做错了什么?
    • @Nikhil 这个问题和答案是关于删除旧的(过时的)权限。如果您尝试从现有模型中删除默认权限,您可能希望将 Model.Meta.default_permissions 设置为空列表。 docs.djangoproject.com/en/4.0/ref/models/options/…
    猜你喜欢
    • 2020-01-01
    • 2015-11-06
    • 1970-01-01
    • 1970-01-01
    • 2015-12-16
    • 1970-01-01
    • 2018-11-05
    • 2020-01-24
    • 1970-01-01
    相关资源
    最近更新 更多