Django:调整@login_required 装饰器

Posted

技术标签:

【中文标题】Django:调整@login_required 装饰器【英文标题】:Django: Tweaking @login_required decorator 【发布时间】:2011-08-06 10:06:31 【问题描述】:

我想为我的网站开始一个私人测试版。我有一个启动页面,用户可以在其中输入代码然后访问站点的其余部分。目前,所有其他网站页面(除了启动页面)都包含一系列通过要求用户登录(通过 @login_required 装饰器)设置的重定向。

我希望登录用户和输入 Beta 测试人员代码的用户都能够访问网站的其余部分。这意味着我不能只将装饰器用于我的所有视图。

我应该改变@login_required 装饰器本身吗?我更想做以下事情(如果用户在启动页面上输入正确的代码,我添加了一个会话变量)。

def view_name(request):
    user=request.user  
    if not user.id or not request.session.get('code_success'):
           return HttpResponseRedirect('/splash/')

这看起来合理吗?我不想为我的所有观点重复它

布伦丹

【问题讨论】:

【参考方案1】:

编写您自己的装饰器 - 相当简单。实际上,如果您查看 login_required 的 Django 源代码,您应该能够为自己的目的摆弄一个副本。

def my_login_required(function):
    def wrapper(request, *args, **kw):
        user=request.user  
        if not (user.id and request.session.get('code_success')):
            return HttpResponseRedirect('/splash/')
        else:
            return function(request, *args, **kw)
    return wrapper

【讨论】:

嗨史蒂夫,这似乎是一个很好的解决方案。我试了一下。首先,我得到一个回溯,告诉我 renderer() 正在接受一个论点。然后我尝试添加可选的 args/kwargs (renderer(*args, *kwargs)),我得到了一个几乎无法辨认的回溯,声称存在 MiddleWare 错误。然后我从整个函数定义中剥离了 renderer() ,只留下了包装器。这行得通,尽管它无法识别 request.session.get('code_success') 我已经编辑了解决方案(对于看到它的其他人) - 现在看起来正确吗? 是的,现在可以了,史蒂夫。不过,我有一个问题。当我查看其他装饰器时,我经常看到这个 ////return wraps(func)(wrapper)///。看起来 wraps 函数在这里并不相关,但我看过一个类似的教程。那里有什么故事? 我不确定我是否见过这样做的案例 - 你有例子吗? 链接到 Django 源:docs.djangoproject.com/en/dev/_modules/django/contrib/auth/…【参考方案2】:

我建议改用中间件。一旦您退出私人测试版,这将使您更容易放弃。在 djangonsippets 上有几个需要登录的中间件示例:

http://djangosnippets.org/snippets/1220/http://djangosnippets.org/snippets/136/

我建议采用其中之一并对其进行调整以包含您的 beta 代码逻辑。

【讨论】:

嗨,马克,这是有道理的。我还没有摆弄使用非标准中间件。 RequireLoginMiddleWare 很酷,肯定会为我节省一些时间。我要试试这两个。谢谢!【参考方案3】:

如何重用(调整)内部 Django login_required

例如,您需要只允许通过 login_required 检查并且也是教练的用户访问页面 - 并且(保存)将教练实例传递给您查看进一步处理

装饰器.py

from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect

from profiles.models import CoachProfile


def coach_required(function):
    def wrapper(request, *args, **kwargs):
        decorated_view_func = login_required(request)
        if not decorated_view_func.user.is_authenticated():
            return decorated_view_func(request)  # return redirect to signin

        coach = CoachProfile.get_by_email(request.user.email)
        if not coach:  # if not coach redirect to home page
            return HttpResponseRedirect(reverse('home', args=(), kwargs=))
        else:
            return function(request, *args, coach=coach, **kwargs)

    wrapper.__doc__ = function.__doc__
    wrapper.__name__ = function.__name__
    return wrapper

views.py

@coach_required
def view_master_schedule(request, coach):
    """coach param is passed from decorator"""
    context = 'schedule': coach.schedule()
    return render(request, 'template.html', context)

【讨论】:

【参考方案4】:

我会创建一个访客帐户,然后将 Beta Tester 代码输入该帐户的用户登录。大致如下:

def beta_code_accepted(request):
    guest_user = User.objects.get(username='beta_guest')
    login(request, guest_user)

您的测试版完成后,只需禁用启动视图即可。

【讨论】:

对,但我也希望来宾用户能够注册一个完整的帐户。通常,我从登录用户的注册页面重定向。我想我可以允许登录用户注册,这会奏效。我看不出这有什么问题,对吧? 这里的问题是我的应用程序的其他部分有依赖于 user.is_authenticated 上下文变量的逻辑。我不希望测试版的客人能够对某些事情进行烫发。然后,我必须在此方法之上添加一个权限层。 我明白了。也许您可以修改注册页面以检查登录的访客用户。如果您检测到访客帐户,请向他们显示一条消息,建议您注册一个完整帐户或继续以访客身份。

以上是关于Django:调整@login_required 装饰器的主要内容,如果未能解决你的问题,请参考以下文章

使用 @login_required 时的 Django 缓存

Django @login_required 用于类视图

Django @login_required 删除 https

Django:使用@login_required 在视图上测试失败

Django 中的 login_required 装饰器和 urlresolver.reverse()

带有 login_required 装饰器的 Django 3.1 异步视图