2013-04-22 106 views
0

根据几个other SO answers,我使用中间件在我的项目的每个页面上显示登录表单,以便用户可以在原地登录。我知道有些人对此感到不满,但它的确提供了更好的用户体验。理想情况下它会是异步的,但我还没有到达那里。Django LoginForm中间件基于类视图

反正middleware.py:

from MyProj.forms import MyProjTopLoginForm 
from django.http import HttpResponseRedirect 
from django.core.urlresolvers import reverse 

#Notes: https://stackoverflow.com/questions/2734055/putting-a-django-login-form-on-every-page 
class LoginFormMiddleware(object): 

    def process_request(self, request): 

     # if the top login form has been posted 
     if request.method == 'POST' and 'top_login_form-username' in request.POST: 

      # validate the form 
      form = MyProjTopLoginForm(prefix="top_login_form", data=request.POST) 
      if form.is_valid(): 

       # log the user in 
       from django.contrib.auth import login 
       login(request, form.get_user()) 

       # if this is the logout page, then redirect to/
       # so we don't get logged out just after logging in 
       if reverse('logout') in request.get_full_path(): 
        return HttpResponseRedirect('/') 
       #return HttpResponseRedirect('/') 

     else: 
      form = MyProjTopLoginForm(request, prefix="top_login_form") 

     # attach the form to the request so it can be accessed within the templates 
     request.top_login_form = form 

class LogoutFormMiddleware(object): 
    def process_request(self, request): 
     if request.method == 'POST' and request.POST.has_key('logout-button') and request.POST['logout-button'] == 'logout': 
      from django.contrib.auth import logout 
      logout(request) 
      request.method = 'GET' 

和views.py:

from django.shortcuts import render, redirect 
from django.contrib.auth.decorators import login_required 
from django.db.models import Count 
from django.core.urlresolvers import reverse 

from django.views.generic import TemplateView 

class Index(TemplateView): 
    template_name = 'KReport2/index.djhtml' 

''' 
def index(request): 
    return render(request, 'KReport2/index.djhtml') 
''' 

def logoutpage(request): 
    from django.contrib.auth import logout 
    logout(request) 
    return redirect(index) 

你会注意到,我的评论旧索引视图功能,并以团体取代了它基于视图。通常我不会继承子类,而是在urls.py中传递template_name,但这里除了这一点之外。我的问题似乎是中间件断了。在索引页填写UN/Pass,然后提交,中间件捕获表单并将我登录,但是然后django继续返回一个空白的http响应。没有错误,但也没有渲染索引页面。我只是不知道休息是在哪里发生的。

回答

1

在基于视图的类中,默认的dispatch方法试图委托给与HTTP方法对应的方法。

TemplateView只实现了get()方法,所以它只适用于GET请求。当您使用POST请求登录时,调度方法将查找TemplateView.post()方法。由于这不存在,它将返回HTTP错误405(方法不允许)。

在您的中间件中,我建议您在成功登录后重定向到相同的URL。这个Post/Redirect/Get模式是一般的好建议。浏览器将遵循重定向,并通过GET请求获取IndexView,这将会成功。

if form.is_valid(): 
    # log the user in 
    from django.contrib.auth import login 
    login(request, form.get_user()) 

    # if this is the logout page, then redirect to/
    # so we don't get logged out just after logging in 
    if reverse('logout') in request.get_full_path(): 
     return HttpResponseRedirect('/') 
    # redirect to the same url after a successful POST request. 
    return HttpResponseRedirect('') 

最后,浏览器可能会显示一个空白页面,但有更多信息可用于调试。 Django dev服务器将显示它返回了一个405错误代码。使用适用于您的浏览器的开发人员工具栏,该工具栏应显示错误代码405 METHOD NOT ALLOWED的描述以及Allow:get, head标题,该标题会告诉您该视图不允许发布发布请求。

+0

巨大!很好的回答,谢谢。虽然我发现LogoutFormMiddleware已经解决了这个问题,只需在注销之后通过设置request.method ='GET'来完成,这样视图就可以通过GET方法结束处理,从而解决问题。在我看来,最好是发布重定向到相同的URL,虽然我不确定优点会是什么(可能是一点速度) – 2013-04-23 22:45:16

+0

不重定向理论上更快,但我不知道如果用户会注意到实践中的任何差异。改变中间件中的'request.method'对我来说似乎很难受。就个人而言,我会避免这样做。在成功发布帖子后重新定向是一个很好的习惯。在这种情况下,这不是必需的,因为没有风险例如两次处理付款。如果用户在登录后立即刷新页面,重定向将阻止用户获得“是否要重新提交请求”确认消息。我认为这是一个可用性改进。 – Alasdair 2013-04-24 00:13:20

+0

是的,我完全同意,很好的捕捉。重定向它。 – 2013-04-25 00:18:40

0

为了最终确定这个问题,Alasdair的答案是现货。我用最后的代码是

from MyProj.forms import MyProjTopLoginForm 
from django.http import HttpResponseRedirect 
from django.core.urlresolvers import reverse 

#Notes: http://stackoverflow.com/questions/2734055/putting-a-django-login-form-on-every-page 
class LoginFormMiddleware(object): 

    def process_request(self, request): 

     # if the top login form has been posted 
     if request.method == 'POST' and 'top_login_form-username' in request.POST: 

      # validate the form 
      form = MyProjTopLoginForm(prefix="top_login_form", data=request.POST) 
      if form.is_valid(): 

       # log the user in 
       from django.contrib.auth import login 
       login(request, form.get_user()) 

       # if this is the logout page, then redirect to/
       # so we don't get logged out just after logging in 
       if reverse('logout') in request.get_full_path(): 
        return HttpResponseRedirect('/') 
       # Redirect to the same page after successfully handling the login form data. 
       return HttpResponseRedirect('') 
       # We could also do: 
       # request.method = 'GET' 
       # instead of a redirect, but if a user refreshes the page, they'll get prompted to re-send post data, 
       # which is super annoying. 

     else: 
      form = MyProjTopLoginForm(request, prefix="top_login_form") 

     # attach the form to the request so it can be accessed within the templates 
     request.top_login_form = form 

class LogoutFormMiddleware(object): 
    def process_request(self, request): 
     if request.method == 'POST' and request.POST.has_key('logout-button') and request.POST['logout-button'] == 'logout': 
      from django.contrib.auth import logout 
      logout(request) 
      # Same as above. Handle the post data, then redirect to a new GET (not POST) request. 
      return HttpResponseRedirect('') 

这解决了原来的问题,以及在发行的(登录,注销)的数据,以便用户触发的刷新不提示重新发送成功处置重定向表格。

相关问题