0

As per a few other SO answers, I'm using middleware to show a login form on every page of my project, such that a user can login in-place. I am aware some frown upon this, but it really does make for a much better user experience. Ideally it'd be asynchronous, but I haven't gotten there.

Anyway, 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'

and 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)

You'll note that I've commented the old index view function and replaced it with a class-based view. Normally I wouldn't subclass, but instead pass template_name in urls.py, but that's besides the point here. My problem seems to be that the middleware breaks. Filling in UN/Pass on the index page, then submitting, the middleware captures the form and logs me in, but then django goes on to return a blank http response. No error, but also no rendered index page. I've just got no idea where the break is happening.

4

2 回答 2

1

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

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

在您的中间件中,我建议您在成功登录后重定向到相同的 url。这个Post/Redirect/Get模式通常是很好的建议。浏览器将跟随重定向,并IndexView使用 GET 请求获取,这将成功。

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 开发服务器将显示它返回了 405 错误代码。使用浏览器的开发人员工具栏,它应该会显示错误代码的描述405 METHOD NOT ALLOWED,以及Allow:get, head告诉您视图不允许发布请求的标题。

于 2013-04-22T21:48:48.523 回答
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('')

这解决了原始问题,并在成功处理(登录、注销)数据时发出重定向,以便用户触发的刷新不会提示重新发送表单。

于 2013-04-25T00:23:43.903 回答