2013-04-22 71 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('') 

這解決了原來的問題,以及在發行的(登錄,註銷)的數據,以便用戶觸發的刷新不提示重新發送成功處置重定向表格。