如何避免 django 权限字符串中的拼写错误

Posted

技术标签:

【中文标题】如何避免 django 权限字符串中的拼写错误【英文标题】:How to avoid typos in django's permission strings 【发布时间】:2016-10-10 08:46:40 【问题描述】:

根据文档,可以像这样创建和使用自定义权限:

class Task(models.Model):
    ...
    class Meta:
        permissions = (
            ("view_task", "Can see available tasks"),
        )

使用权限:

user.has_perm('app.view_task')

来源:https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#custom-permissions

如果权限字符串中有错字。例如:我用user.has_perm('app.view_tasks'),那么这个错字就不会被注意到了。

目标 1

如果没有使用现有权限,我想获得异常或警告。

目标 2

首先为了避免拼写错误,我希望有常量:user.PERM_APP_VIEW_TASKS 或类似的东西。

【问题讨论】:

那么实际的问题是什么?对于目标 1,您可以更改用户的 has_perm 方法,以便使用 Permission.objects.get(...) 检查数据库中是否存在权限,对于目标 2,您可以创建类变量 @SardorbekImomaliev 问题是“如何以最 Pythonic 的方式解决这个问题?”。 【参考方案1】:

目标 1

从我的backends.py 文件中重写ModelBackend 类的has_perm 方法:

import logging
from difflib import get_close_matches
from django.conf import settings
from django.contrib.auth.backends import ModelBackend


class ModelBackendHelper(ModelBackend):
    def has_perm(self, user_obj, perm, obj=None):
        if not user_obj.is_active:
            return False
        else:
            obj_perms = self.get_all_permissions(user_obj, obj)
            allowed = perm in obj_perms

            if not allowed:
                if settings.DEBUG:
                    similar = get_close_matches(perm, obj_perms)

                    if similar:
                        logging.warn("0 not found, but is similar to: 1".format(perm, ','.join(similar)))

            return allowed

它是如何工作的:

has_perm 的逻辑相同,但如果settings.DEBUGTrue 并找到类似版本的perm,则输出级别为WARN 的警告日志消息:

WARNING:root:myapp.view_tasks not found, but is similar to: myapp.view_task

更改settings.pyAUTHENTICATION_BACKENDS的值:

AUTHENTICATION_BACKENDS = ['myapp.backends.ModelBackendHelper']

这可以在生产和开发环境中使用,但我个人不会将它包含在生产站点中,我希望当一切都进入生产时,权限得到整合。

目标 2

权限属于模型,为了保持这个 DRY,我重用了 Meta 中定义的权限:

from django.db import models


class Task(models.Model):
    name = models.CharField(max_length=30)
    description = models.TextField()

    class Meta:
        permissions = (
            ("view_task", "Can see available tasks"),
        )


def _get_perm(model, perm):
    for p in model._meta.permissions:
        if p[0] == perm:
            return p[0]
    err = "Permission '0' not found in model 1".format(perm, model)
    raise Exception(err)


def format_perm(perm):
    return '0.1'.format(__package__, perm)


def get_perm(model, type):
    return format_perm(_get_perm(model, type))


PERM_APP_VIEW_TASK = get_perm(Task, "view_task")

可以使用get_perm 或快捷方式PERM_APP_VIEW_TASK 访问权限:

models.PERM_APP_VIEW_TASK
# or
get_perm(Task, "view_task")
# or
format_perm(_get_perm(Task, "view_task"))

如果搜索缺少权限get_perm 将引发Exception

PERM_APP_VIEW_TASK = get_perm(Task, "add_task")

消息:

Exception: Permission 'add_task' not found in model <class 'myapp.models.Task'>

【讨论】:

当用户没有权限时,即使权限拼写正确,这不是在所有情况下都显示警告吗? 如果您使用多个AUTHENTICATION_BACKENDS,例如使用 django-allauth,这将如何工作?你不会也覆盖 allauths 实现吗?【参考方案2】:

目标 1

创建自定义装饰器:

警告!未经测试的代码!!!!!!

@permission_required
def assert_permission(permission):
    def real_decorator(original_function):
        def wrapper(request, *args, **kwargs):
            permission_object = Permission.objects.filter(codename=permission)
            if not permission_object.exists():
                raise ValueError("This permission does not exist!")
            original_function(request, *args, **kwargs)
        return wrapper
    return real_decorator

目标 2

向您的模型类添加常量,并在请求特定权限时引用这些常量。 示例:

class Task(models.Model):
MY_PERMISSION_CONSTANT = 'app.view_task'
...
class Meta:
    permissions = (
        (MY_PERMISSION_CONSTANT, "Can see available tasks"),
    )

@permission_required(Task.MY_PERMISSION_CONSTANT)
def some_view(request):
  pass

【讨论】:

你确定Goal1不能实现吗?对我来说,数据库中的所有权限都是有效的。每个与 db 行不对应的字符串都是无效的。 @guettli 无法对此进行测试,但我认为这应该可以工作,或者至少让您朝着正确的方向前进,如果某些事情不起作用并且您修复了它,请编辑此回答;)

以上是关于如何避免 django 权限字符串中的拼写错误的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Django 1.9 中删除 DB (sqlite3) 以从头开始?

.Net 项目中的拼写检查消息字符串

如何在 Django 自定义数据库函数调用周围避免 SQL 中的括号?

单词拼写检查之cutoff距离

iOS输入限制字数时,避免与中文拼音拼写板冲突

如何在 R 中迭代以同时保存多个文件并避免“absolute_path(target) 中的错误:'x' 必须是单个字符串”?