utils.admin_utils 源代码

from typing import Union, Callable, Optional
from functools import wraps, update_wrapper

from django.db import transaction
from django.db.models import QuerySet, Model
from django.contrib import messages
from django.contrib.auth import get_permission_codename
from django.contrib.admin import ModelAdmin, SimpleListFilter

from utils.http import HttpRequest

__all__ = [
    'as_display', 'as_action',
    'no_perm', 'has_superuser_permission',
    'inherit_permissions',
    'perms_check', 'need_all_perms',
    'readonly_admin', 'readonly_inline',
    'SimpleSignFilter', 'get_sign_filter',
]


def _as_perms(permissions: 'str | list[str]'):
    if isinstance(permissions, str):
        return [permissions]
    return permissions


[文档] def as_display(description=None, /, register_to=None, *, except_value=None, boolean=None, order=None): ''' 将函数转化为展示内容的形式,并试图注册(register_to只应是list,否则会出错) 可以设置输出的形式,异常时的呈现值,呈现顺序等 ''' def actual_decorator(action_function): @wraps(action_function) def _wrapped_display(self: ModelAdmin, obj): try: return action_function(self, obj) except: if except_value is None: raise return except_value if boolean is not None: _wrapped_display.boolean = boolean if order is not None: _wrapped_display.admin_order_field = order if description is not None: _wrapped_display.short_description = description if register_to is not None and _wrapped_display.__name__ not in register_to: register_to.append(_wrapped_display.__name__) return _wrapped_display return actual_decorator
[文档] def as_action(description: str = None, /, register_to: list = None, permissions: 'str | list[str]' = None, *, superuser: bool = None, single: bool = False, atomic: bool = False, update: bool = False): ''' 将函数转化为操作的形式,并试图注册 检查用户是否有权限执行操作,有权限时捕获错误 权限是列表,单个权限可只传入字符串,提供权限要求时默认不检查是否为超级用户 关键字参数进行检查或启用必要的环境 ''' def actual_decorator(action_function): nonlocal superuser if superuser is None: superuser = (permissions is None) @wraps(action_function) def _wrapped_action(self: ModelAdmin, request, queryset): if superuser and not request.user.is_superuser: return self.message_user(request=request, message='操作失败,没有权限,请联系老师!', level=messages.WARNING) if single and len(queryset) != 1: return self.message_user(request=request, message='仅允许操作单个条目!', level=messages.WARNING) try: if atomic or update: with transaction.atomic(): if update: queryset = queryset.select_for_update() return action_function(self, request, queryset) return action_function(self, request, queryset) except Exception as e: return self.message_user(request=request, message=f'操作时发生{type(e)}异常: {e}', level=messages.ERROR) if permissions is not None: _wrapped_action.allowed_permissions = _as_perms(permissions) if description is not None: _wrapped_action.short_description = description if register_to is not None and _wrapped_action.__name__ not in register_to: register_to.append(_wrapped_action.__name__) return _wrapped_action return actual_decorator
PermFunc = Callable[[ModelAdmin, HttpRequest, Optional[Model]], bool]
[文档] def no_perm(self: ModelAdmin, request: HttpRequest, obj=None): '''总是返回没有权限''' return False
[文档] def has_superuser_permission(self: ModelAdmin, request: HttpRequest, obj=None): '''检查是否为超级用户''' return request.user.is_superuser
def has_perm(action: str, model: Model = None) -> PermFunc: ''' 检查模型的指定权限 :param action: 需要检查的权限名 :type action: str :param model: 权限模型, defaults to None :type model: Type[Model], optional :return: 权限检查函数 :rtype: PermFunc ''' if model is not None: codename = get_permission_codename(action, model._meta) perm = f'{model._meta.app_label}.{codename}' def _check_func(self: ModelAdmin, request: HttpRequest, obj=None) -> bool: return request.user.has_perm(perm) return _check_func # 否则执行时计算 def _check_func(self: ModelAdmin, request: HttpRequest, obj=None) -> bool: codename = get_permission_codename(action, self.opts) return request.user.has_perm(f'{self.opts.app_label}.{codename}') return _check_func
[文档] def inherit_permissions(model: Model, superuser: bool = True): ''' 包装器,根据关联模型, 被包装的模型的action除四种自带权限以外,还可使用superuser和模型声明的各种权限 实现细节:为后台自动生成has_%perm%_permission权限检查函数,不覆盖已存在函数 :param model: 后台的关联模型 :type model: Type[Model] :param superuser: 是否产生has_superuser_permission检查函数, defaults to True :type superuser: bool, optional ''' def _actual_wrapper(admin: ModelAdmin): for perm in model._meta.permissions: check_name = f'has_{perm}_permission' if hasattr(admin, check_name): continue setattr(admin, check_name, has_perm(perm, model)) if superuser and not hasattr(admin, 'has_superuser_permission'): admin.has_superuser_permission = has_superuser_permission return admin return _actual_wrapper
[文档] def perms_check(necessary_perms: Union[str, list]=None, optional_perms: Union[str, list]=None, *, superuser=False): '''检查函数,必须具有全部必要权限和至少一个可选权限(如果非空),单个权限可为字符串''' def _check_func(self: ModelAdmin, request: HttpRequest): if superuser and not request.user.is_superuser: return False necessary_checks = ( getattr(self, f'has_{perm}_permission') for perm in _as_perms(necessary_perms) or [] ) if not all(has_perm(request) for has_perm in necessary_checks): return False optional_checks = ( getattr(self, f'has_{perm}_permission') for perm in _as_perms(optional_perms) or [] ) if any(has_perm(request) for has_perm in optional_checks): return True elif optional_checks: return False return True return _check_func
[文档] def need_all_perms(necessary_perms: Union[str, list]=None, optional_perms: Union[str, list]=None, *, superuser=False): '''包装器,完全取代被包装函数,通过对象的has_$perm$_permission方法检查''' def actual_decorator(check_function): _check_func = perms_check( necessary_perms, optional_perms, superuser=superuser, ) update_wrapper(_check_func, check_function) return _check_func return actual_decorator
[文档] def readonly_admin(admin: ModelAdmin, inline_attrs: bool = True, can_add: bool = False): '''将管理模型设为只读,inline_attrs决定是否设置内联模型相关属性,通常无影响''' # 设置内联模型的属性,对通用管理模型无影响,一般都可以设置 if inline_attrs: admin.extra = 0 admin.can_delete = False if not can_add: if admin.fields is not None: admin.readonly_fields = admin.fields admin.has_add_permission = no_perm admin.has_change_permission = no_perm admin.has_delete_permission = no_perm return admin
readonly_inline = readonly_admin
[文档] class SimpleSignFilter(SimpleListFilter): '''子类必须以field定义筛选的字段,可以定义lookup_choices''' title = '符号' parameter_name = 'Sign' field: str = NotImplemented
[文档] def lookups(self, request, model_admin): return getattr(self, 'lookup_choices', (('+', '正'), ('-', '负'), ('=', '零')))
[文档] def queryset(self, request, queryset: QuerySet): if self.value() == '+': return queryset.filter(**{self.field + '__gt': 0}) elif self.value() == '-': return queryset.filter(**{self.field + '__lt': 0}) elif self.value() == '=': return queryset.filter(**{self.field + '__exact': 0}) return queryset
[文档] def get_sign_filter(field: str, title: str = None, param_name: str = None, choices: 'tuple[tuple[str, str]]' = None): class SignFilter(SimpleSignFilter): pass SignFilter.field = field if title is not None: SignFilter.title = title if param_name is not None: SignFilter.parameter_name = param_name if choices is not None: SignFilter.lookup_choices = choices return SignFilter