如何避免 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.DEBUG
是True
并找到类似版本的perm
,则输出级别为WARN
的警告日志消息:
WARNING:root:myapp.view_tasks not found, but is similar to: myapp.view_task
更改settings.py
中AUTHENTICATION_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) 以从头开始?
如何在 Django 自定义数据库函数调用周围避免 SQL 中的括号?
如何在 R 中迭代以同时保存多个文件并避免“absolute_path(target) 中的错误:'x' 必须是单个字符串”?