我的一些观点有限制访问的装饰器,如下所示:
@user_passes_test(my_validation_function)
def my_restricted_view(request):
...
问题是,在我的模板中,我想根据my_validation_function
中的逻辑隐藏用户无权访问的链接。
我知道这样做的一种方法是定义一个基本上调用my_validation_function
(例如my_validation_filter
)的自定义过滤器,并相应地显示/隐藏链接。像这样:
{% if request | my_validation_filter %}
<a href="{% url 'my_restricted_view' %}"></a>
{% endif %}
我在这里看到的问题是我将验证链接两次:一次在视图中,一次在模板中。假设我有很多视图,每个视图后面都有不同的验证逻辑:
@user_passes_test(my_validation_function)
def my_restricted_view(request):
...
@user_passes_test(my_other_validation_function)
def my_other_restricted_view(request):
...
这意味着,当我编写模板时,我必须要小心,始终记住哪个验证功能与哪个视图一致。
有没有办法定义一个函数或反转URL,然后检查视图装饰器中定义的验证?我在想这样的事情:
{% if can_access 'my_restricted_view' %}
{# this implicitly calls 'my_validation_function' #}
...
{% endif %}
{% if can_access 'my_other_restricted_view' %}
{# this implicitly calls 'my_other_validation_function' #}
...
{% endif %}
基本上我想要的只是在一个地方改变每个视图的验证逻辑,而不是触摸我的模板。
答案 0 :(得分:1)
你的问题非常有趣,我没有完整的答案,但有些曲目。
首先,很难,也许不可能从装饰函数中获取装饰器,例如 acc_cfg.cred_info[0].username = pj_str((char*)uname);
acc_cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
acc_cfg.cred_info[0].data = pj_str((char *)passwd);
acc_cfg.cred_info[0].realm = pj_str("*");
acc_cfg.cred_info[0].scheme=pj_str((char*)"Digest");
char regUri[PJSIP_MAX_URL_SIZE];
sprintf(regUri, "sip:%s", sip_server);
acc_cfg.reg_uri = pj_str(regUri);
acc_cfg.ipv6_media_use = PJSUA_IPV6_ENABLED;
acc_cfg.transport_id = transport_id_udp6;
,我不知道如何做。但是您可以将验证功能从装饰器移动到装饰功能。替换这个:
inspect
由此:
@user_passes_test(my_validation_function)
def my_restricted_view(request):
...
在装饰器的代码中处理这种变化应该很容易。
然后您可以编写一个自定义过滤器,您可以将其称为:
@user_passes_test
def my_restricted_view(request):
...
my_restricted_view.validation_function = my_validation_function
此过滤器的代码可能如下所示:
{% if request|validation_filter:'my_restricted_view' %}
答案 1 :(得分:0)
@albar和@Jamie Sanz之间的交流使我走上了正确的道路。下面的完整解决方案,也链接到要旨(https://gist.github.com/solace/9cfae6cb9c60658857ee73f05d5b715a)。与随附的模板标签相比,user_passes_test
略有增加。
views.py
from .decorators import user_passes_test
from .utils import perms_check
# pass `test_func` and related test params
@user_passes_test(perms_check, 'a', ...)
def secured(request):
pass
decorators.py
# Copy of `django.contrib.auth.decorators.user_passes_test`, relevant changes noted.
# CHANGED: `*test_args` for the inclusion of `test_func` arguments
def user_passes_test(test_func, *test_args, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
# CHANGED: Pass `*test_args` to the `test_func`
if test_func(request.user, *test_args):
return view_func(request, *args, **kwargs)
path = request.build_absolute_uri()
resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
return redirect_to_login(
path, resolved_login_url, redirect_field_name)
# ADDED: set the `test_func` and `test_args` on the view function to use later
_wrapped_view.can_view_validator = (test_func, *test_args)
return _wrapped_view
return decorator
permissions.py
from django import template
from django.core.exceptions import PermissionDenied
from django.urls import resolve, reverse
register = template.Library()
# Creates a template tag that looks like this:
# {{ can_view 'my_app:secured' as can_view_secured }}
@register.simple_tag(takes_context=True)
def can_view(context, named):
# `reverse` the named url to get the url, `resolve` it to get the view function
# `user_passes_test` returns True/False or redirects, but `test_func` could also
# raise a `PermissionDenied`. So handle that, too.
match = resolve(reverse(named))
try:
if hasattr(match.func, 'can_view_validator'):
(test_func, *args) = match.func.can_view_validator
return test_func(context.request.user, *args)
else:
return True
except PermissionDenied:
return False
some_template.html
{% load permissions %}
<ul class="menu">
...
{% can_view 'my_app:secured' as can_view_secured %}
{% if can_view_secured %}
<li>
<a href="{% url 'my_app:secured' %}">Secured Link</a>
</li>
{% endif %}
...
</ul>