指定Django login_required装饰器传递给登录视图的`next`网址 [英] Specify `next` url passed to login view by Django login_required decorator

查看:64
本文介绍了指定Django login_required装饰器传递给登录视图的`next`网址的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

来自 Django文档我知道,当您在视图上使用 @login_required 装饰器时,如果用户未登录,Django将会:


重定向到 settings.LOGIN_URL ,在查询字符串中传递当前的绝对路径。


因此,如果从url / foo / bar / 调用修饰的视图,它们将被重定向到:

  / login /?next = / foo / bar / 

我想知道是否有一种很好的方法为特定视图指定'next'路径,而不是使用默认值或在 settings.LOGIN_REDIRECT_URL


我当前建立网站的方式(我不确定这是否是最好的方式)我有一个项目详细信息页面,以及一个用户何时尝试对项目执行操作(即 P OST 表单数据并更改数据库中的项目记录),但它们尚未登录,它将它们重定向到登录页面。


我的问题是为 next 参数传递的路径是 POST ing形式的路径,我希望用户而是重定向到他们之前所在的商品详细信息页面。有没有一种方法可以指定将哪个URL作为 next 参数传递,而不是使用默认值或在 settings.py

解决方案

此限制仅适用于 / admin / Django部分。您可以编写自己的登录视图,该视图重定向到您想要的任何位置(示例在本文的底部)。



当前的 admin 功能似乎是围绕着这样的想法写的,即管理员应始终返回到输入的URL,然后将其重定向到登录页面。



要更改此行为管理员,这些是您的选择,如我所见。



自定义login_required装饰器



要更改默认行为,请添加login_required功能的下一个关键字arg。这将需要重写在 django.contrib.auth.decorators 中找到的login_required函数和user_passes_test帮助程序函数。根据原始代码,它可能类似于:

  def user_passes_test(test_func,next = None,login_url = None,redirect_field_name = REDIRECT_FIELD_NAME):

装饰器,用于检查用户是否通过了给定的测试,如有必要,
重定向到登录页面。该测试应为可调用的
接受用户对象,如果用户通过则返回True。


def装饰器(view_func):
@wraps(view_func,assigned = available_attrs(view_func) )
def _wrapped_view(request,* args,** kwargs):
if test_func(request.user):
return view_func(request,* args,** kwargs)
path = request.build_absolute_uri()
resolve_login_url = resolve_url(login_url或settings.LOGIN_URL)
#如果登录网址是相同的方案和网络位置,则只需
#将该路径用作下一个网址。
login_scheme,login_netloc = urlparse(resolved_login_url)[:2]
current_scheme,current_netloc = urlparse(path)[:2]
if((not login_scheme或login_scheme == current_scheme)和
(不是login_netloc或login_netloc == current_netloc)):
路径= request.get_full_path()
resolve_next_url = resolve_url(下一个或路径)
来自django.contrib.auth.views导入redirect_to_login
return redirect_to_login(
resolve_next_url,resolve_login_url,redirect_field_name)
return _wrapped_view
返回装饰器


def login_required(function = None,next =无,redirect_field_name = REDIRECT_FIELD_NAME,login_url =无):

装饰器,用于检查用户是否已登录,并在必要时将
重定向到登录页面。 $ b
Actual_decorator = user_passes_test(
lambda u:u.is_authenticated,
next = next
login_url = login_url,
redirect_field_name = redirect_field_name,

if函数:
return actual_decorator(function )
returnual_decorator

在上面的代码中,我添加了关键字args 下一步并添加了下一个URL解析。 Django的所需的原始login_可以在此处找到。 / p>

这将允许您执行以下操作:

  @login_required(next = 'app:view')
def profile(request):
....



手动输入下一个查询参数



或者,您也可以手动设置已设置下一个参数的登录网址,但这似乎不适用于您的情况。

 < a href = / login /?next = / app / view / />登录< / a> 



创建一个替换下一个参数的中间件



如果您打算重定向到单个URL(也许也可以重定向到两个URL),则可以选择使用中间件。确保将此中间件放在AuthenticationMiddleware之前。中间件如下所示:

  class LoginNextMiddleware(object):
redirect_url ='/ app / view /'

def process_request(self,request):
next_url = request.GET.get('next',None)
如果next_url不为None并且next_url!= self.redirect_url :
params = dict(request.GET.copy())
del params ['next']
params ['next'] = self.redirect_url
返回重定向(请求.path +'?'+ urlencode(params))



编写自定义登录视图



最后,如果您想灵活地编写自定义登录视图,则是一种方法。看起来像这样:

  def login_user(request):
logout(request)
username =密码=''
(如果有请求).POST:
用户名= request.POST ['用户名']
密码= request.POST ['密码']
用户= authenticate(用户名= username,password = password)
如果user不是None并且user.is_active:
login(request,user)
return HttpResponseRedirect(reverse('app:view'))
返回render_to_response('login.html',context_instance = RequestContext(request))

自定义表单,例如:

 < form action = {{request.get_full_path}} method = post> ; 
{%csrf_token%}
<输入类型= hidden name = next value = {{request.get_full_path}} />
<输入类型=文本自动对焦名称=用户名 />
<输入类型=密码 name =密码 />
< button type = submit>登录< / button>
< / form>


From the Django documentation I know that when you use the @login_required decorator on a view, if the user is not logged Django will:

redirect to settings.LOGIN_URL, passing the current absolute path in the query string.

So if the decorated view is called from the url /foo/bar/ they would be redirected to:

/login/?next=/foo/bar/ 

I am wondering if there is a good way to specify the 'next' path for a particular view instead of using the default or hardcoding it in settings.LOGIN_REDIRECT_URL.

The way my site is currently set up (I am not sure if this is the best way to do it) I have an item detail page, and when a user tries to act on the item (i.e. POST form data and change the item record in the database) but they are not logged in, it redirects them to the login page.

My problem is that the path that is passed for the next parameter is the path for the form POSTing action, and I would like the user instead to be redirected to the item detail page they were previously on. Is there a way to specify which URL is passed as the next parameter instead of using the default or defining it in settings.py?

解决方案

This limitation is only applicable to the /admin/ section of Django. You could write your own login view that redirects to where ever you want (example at the bottom of this post).

The current admin functionality seems to be written around the idea that the admin should always go back to the entered url that was viewed before being redirected to the login page.

To change this behaviour of the admin these are your options, as i see it.

Custom login_required decorator

To change the default behaviour add a next keyword arg to the login_required functionality. This would require rewriting the login_required function and user_passes_test helper function found in django.contrib.auth.decorators. Based on the original code it could look something like:

def user_passes_test(test_func, next=None, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
    """
    Decorator for views that checks that the user passes the given test,
    redirecting to the log-in page if necessary. The test should be a callable
    that takes the user object and returns True if the user passes.
    """

    def decorator(view_func):
        @wraps(view_func, assigned=available_attrs(view_func))
        def _wrapped_view(request, *args, **kwargs):
            if test_func(request.user):
                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() 
            resolved_next_url = resolve_url(next or path)
            from django.contrib.auth.views import redirect_to_login
            return redirect_to_login(
                resolved_next_url, resolved_login_url, redirect_field_name)
        return _wrapped_view
    return decorator


def login_required(function=None, next=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
    """
    Decorator for views that checks that the user is logged in, redirecting
    to the log-in page if necessary.
    """
    actual_decorator = user_passes_test(
        lambda u: u.is_authenticated,
        next=next
        login_url=login_url,
        redirect_field_name=redirect_field_name,
    )
    if function:
        return actual_decorator(function)
    return actual_decorator

In the code above i've added keywords args next and added next url resolving. Django's original login_required can be found here.

This would allow you to do:

@login_required(next='app:view')
def profile(request):
    ....

Manual next query parameter

Alternatively you could manually create login urls with the next parameter already set, but this seems an approach that would not work in your case.

<a href="/login/?next=/app/view/" />Login</a>

Create a middleware that replaces the next param

If you intend to redirect to a single url - maybe a couple would be possible also -, then a middleware might be an option. Make sure to place this middleware before the AuthenticationMiddleware. A middleware would look something like:

class LoginNextMiddleware(object):
    redirect_url = '/app/view/'

    def process_request(self, request):
        next_url = request.GET.get('next', None)
        if next_url is not None and next_url != self.redirect_url:
            params = dict(request.GET.copy())
            del params['next']
            params['next'] = self.redirect_url
            return redirect(request.path + '?' + urlencode(params))              

Write a custom login view

Finally, if you want flexibility writing a custom login view is the way to go. This would look something like:

def login_user(request):
    logout(request)
    username = password = ''
    if request.POST:
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        if user is not None and user.is_active:
            login(request, user)
            return HttpResponseRedirect(reverse('app:view'))
    return render_to_response('login.html', context_instance=RequestContext(request))

And would require a custom form, something like:

<form action="{{ request.get_full_path }}" method="post">
  {% csrf_token %}
  <input type="hidden" name="next" value="{{ request.get_full_path }}" />
  <input type="text" autofocus name="username" />
  <input type="password" name="password" />
  <button type="submit">Log in</button>
</form>

这篇关于指定Django login_required装饰器传递给登录视图的`next`网址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆