当前位置:首页 > 网站旧栏目 > 学习园地 > 设计软件教程 > 翻译www.djangobook.com之第十二章:会话,用户和注册

翻译www.djangobook.com之第十二章:会话,用户和注册
2010-01-13 23:41:25  作者:  来源:
是时候承认了:我们故意忽略了一个web开发极端重要的方面,到目前为止,我们考虑了大量未露面的匿名用户访问我们
站点页面的流量情况,这当然不正确,访问我们站点的浏览器后面是真实的人(至少有些时候是这样),这是被忽略的一个
大问题:当Internet服务于人而不是机器时是工作的最好的,如果我们开发真正引人注目的站点时,最终我们将不得不与
浏览器后面的人打交道
不幸的是,这并不容易,HTTP被设计为无状态,即每个请求发生在一个空间里,两个请求之间没有持久化,并且我们不能
计算一个请求的每个方面(IP地址,用户代理等等)来一致的显示同一个人的连续请求
浏览器开发人员很久之前就意识到HTTP的无状态导致了web开发人员很大的麻烦,就这样cookies诞生了
cookie是一个小信息片段,浏览器存储它来代表web服务器,每次浏览器从某一服务器请求一个页面时都会把它起初接受
的cookie回传过去

Cookies
让我们看看它可能怎样工作,当你开启浏览器并键入google.com,你的浏览器像这样开始来发送一个HTTP请求到Google:
GET / HTTP/1.1
Host: google.com
...
当Google回复时,HTTP应答看起来像这样:
HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671;
            expires=Sun, 17-Jan-2038 19:14:07 GMT;
            path=/; domain=.google.com
Server: GWS/2.1
注意Set-Cookie头部,你的浏览器将存储这个cookie值(PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671)并在
每次你访问这个站点时回传给Google,所以下一次你访问Google时你的浏览器将传递像这样的请求:
GET / HTTP/1.1
Host: google.com
Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671
...
然后Google可以使用这个Cookie值来知道你是早些时候访问这个站点的同一个人
例如,这个值可能是数据库存储用户信息的键,Google可以(也确实)使用它来在页面上显示你的名字

得到和设置cookies
当在Django中处理持久化时,大部分时候你想使用稍后讨论的高级session和/或用户框架,尽管如此,我们将停下来先
看看在Django中怎样读和写cookies,它将帮助你理解本章其它部分事实上怎样工作,并且如果你需要直接操作cookies
的话它将触手可得
读取已经设置的cookies是非常简单的:每个request对象都有一个类似字典的COOKIES对象,你可以使用它来读浏览器发送
到视图的任何cookies:
Java代码 复制代码
  1. def show_color(request):   
  2.     if "favorite_color" in request.COOKIES:   
  3.         return HttpResponse("Your favorite color is %s" % \   
  4.             request.COOKIES["favorite_color"])   
  5.     else:   
  6.         return HttpResponse("You don't have a favorite color.")  

写cookies更复杂一点,你需要使用HttpResponse对象的set_cookie()方法,这里是一个基于GET参数设置favorite_color
cookie的例子:
Java代码 复制代码
  1. def set_color(request):   
  2.     if "favorite_color" in request.GET:   
  3.   
  4.         # Create an HttpResponse object...   
  5.         response = HttpResponse("Your favorite color is now %s" % \   
  6.             request.GET["favorite_color"])   
  7.   
  8.         # ... and set a cookie on the response   
  9.         response.set_cookie("favorite_color",   
  10.                             request.GET["favorite_color"])   
  11.   
  12.     else:   
  13.         return HttpResponse("You didn't give a favorite color.")  

你也可以传递一些可选参数到request.set_cookie()来控制cookie的一些方面:
Parameter        Default        Description
max_age          None           cookie持续的时间(秒),如果为None,cookie将只持续到浏览器关闭
expires          None           cookie过期的准确日期/时间,格式应为"Wdy, DD-Mth-YY HH:MM:SS GMT"
                                如果给定值,它将覆盖max_age参数
path             "/"            cookie的合法的路径前缀,浏览器将只把cookie传递回该路径前缀下,所以你可以
                                使用它来防止cookies被传递给你的站点的其它部分,当你不控制你的站点域名的
                                顶级部分时这非常有用
domain           None           cookie的合法域名,你可以使用它来设置跨域名的cookie,例如,domain=".examp
                                le.com"将设置一个可以被www.example.com,www2.example.com和an.other.sub.
                                domain.example.com读取的cookie
                                如果设置为None,cookie将只能被设置它的域名读取
secure           False          如果设置为True,它将指示浏览器只当通过HTTPS访问页面时返回这个cookie

cookies的混合祝福
你可能注意到cookies工作的一些潜在的问题,让我们看看一些重要的:
1,Cookies本质上是自发的,浏览器不保证cookies的存储,事实上,这个行星上的每个浏览器都让你控制你的浏览器的
接受cookies的策略,如果你想看重要的cookies怎样到达web,尝试打开浏览器的"接受每个cookie"选项,甚至一个巨大
的蓝色怪物都将填充所有这些cookies!
当然,这意味着cookies上不可信任的定义,开发人员应该检查用户在信赖它们之前接受了cookies
更重要的是,你应该从不在cookies里面存储重要数据,web充满了开发人员为了某些原因在浏览器cookies里存储不可重
获的信息来使浏览器方便的恐怖故事
2,Cookies不是安全的,因为HTTP数据传输的是明文,cookies非常容易受窃听攻击,即攻击者在线上窃听可以截取
cookie并读取它,这意味着你应该从不在cookie里存储敏感信息
还有更阴险的"中间人"攻击,其中一个攻击者截取cookie并使用它来假装为另一个用户,第20章深入讨论了这种攻击现象
并给出了预防的办法
3,Cookies甚至对预定的接受者都不安全,大多数浏览器提供简易方式来编辑单独cookies的内容,并且足智多谋的用户
可以使用像mechanize的工具来手动构建HTTP请求
所以你不能在cookies里存储可窜改的敏感数据,这种情形下的标准错误是当用户登录后在cookie里存储像IsLoggedIn=1
的东西,你会对大量的站点犯这种错误而感到惊奇,只需花一秒钟就可以愚弄这些站点的"安全"系统

Django的session框架
由于这些限制和潜在的安全漏洞,很显然cookies和持久化sessions是另一个web开发里头疼的地方,当然Django的目标是
做高效的头疼杀手,所以Django带来一个为你扫平这些困难的session框架
这个session框架让你基于一个站点访问者存储和得到任意数据,它在服务器端存储数据并抽象发送和接受cookies
Cookies只使用一个哈希session ID而不是数据本身,这样可以防止大部分通常的cookie问题

允许sessions
Sessions通过一个中间件(参考第16章)和一个Django模型实现,你需要做如下事情来允许sessions:
1,编辑你的MIDDLEWARE_CLASSES设置并确认MIDDLEWARE_CLASSES包含'django.contrib.sessions.middleware.Session
Middleware'
2,确认'django.contrib.sessions'在你的INSTALLED_APPS设置里(如果你需要添加它还要允许manage.py syncdb)
通过startproject创建的默认骨架设置已经安装了这两项,所以除非你已经删除了它们,你很可能不需要改变任何东西
就可以让sessions工作
如果你不想使用sessions,你可能想从MIDDLEWARE_CLASSES删除SessionMiddleware行和从INSTALLED_APPS删除
'django.contrib.sessions',它将只保存一个很小的过度,但是这很小的部分起作用

在视图里使用sessions
当SessionMiddleware激活后,每个HttpRequest对象--每个Django视图方法的第一个参数--将有一个session属性,它是
一个类似字典的对象,你可以像使用普通的字典一样读写它,例如,你可以在视图中做这样的事情:
Java代码 复制代码
  1. # Set a session value:   
  2. request.session["fav_color"] = "blue"  
  3.   
  4. # Get a session value -- this could be called in a different view,   
  5. # or many requests later (or both):   
  6. fav_color = request.session["fav_color"]   
  7.   
  8. # Clear an item from the session:   
  9. del request.session["fav_color"]   
  10.   
  11. # Check if the session has a given key:   
  12. if "fav_color" in request.session:   
  13.     ...  

你也可以在request.session使用像keys()和items()的其它映射方法
有一些高效使用Django的sessions的简单规则:
1,在request.session使用普通的Python字符串作为字典的键(而不是integers,objects等等),这是一个惯例,但是值
得遵循
2,以下划线开始的session字典键被Django保留作内部使用,实践中框架只使用非常少的下划线前缀的session变量,但
是除非你知道它们都是些什么(并且想跟上Django本身的更改),最好远离它们以防Django妨碍你的app
3,不要用新的对象覆盖request.session,并且不要访问或者设置它的属性,像Python字典一样使用它
让我们看看一些快速的例子,简单的视图在用户提交一个comment后设置一个has_commented变量为True,它不让一个用户
提交一个comment多于一次:
Java代码 复制代码
  1. def post_comment(request, new_comment):   
  2.     if request.session.get('has_commented', False):   
  3.         return HttpResponse("You've already commented.")   
  4.     c = comments.Comment(comment=new_comment)   
  5.     c.save()   
  6.     request.session['has_commented'] = True   
  7.     return HttpResponse('Thanks for your comment!')  

简单的视图在站点登录一个"成员":
Java代码 复制代码
  1. def login(request):   
  2.     m = members.get_object(username__exact=request.POST['username'])   
  3.     if m.password == request.POST['password']:   
  4.         request.session['member_id'] = m.id   
  5.         return HttpResponse("You're logged in.")   
  6.     else:   
  7.         return HttpResponse("Your username and password didn't match.")  

这个例子根据上面的login()注销一个成员:
Java代码 复制代码
  1. def logout(request):   
  2.     try:   
  3.         del request.session['member_id']   
  4.     except KeyError:   
  5.         pass   
  6.     return HttpResponse("You're logged out.")  

注意,实践中这是登录用户的恶心的方式,下面讨论的认证框架以更健壮和有用的方式为你处理这些,这些内容只是提供
容易理解的例子

设置测试cookies
上面提到,你不能依赖每个浏览器接受cookies,所以,为了方便起见,Django提供了一个简单的方式来测试用户的浏览器
是否接受cookies,你只需在视图中调用request.session.set_test_cookie()并在后面的视图中检查requet.session.test
_cookie_worked(),而不是在同一个视图中调用
由于cookies的工作方式,这样笨拙的分离set_test_cookie()和test_cookie_worked()很必要,当你设置一个cookie,你
事实上不能分辨浏览器是否接受它,直到浏览器下一次请求
你自己使用delete_test_cookie()来清除测试cookie是良好的实践,在你验证测试cookie工作后做这件事
这里是一个典型的使用例子:
Java代码 复制代码
  1. def login(request):   
  2.   
  3.     # If we submitted the form...   
  4.     if request.method == 'POST':   
  5.   
  6.         # Check that the test cookie worked (we set it below):   
  7.         if request.session.test_cookie_worked():   
  8.   
  9.             # The test cookie worked, so delete it.   
  10.             request.session.delete_test_cookie()   
  11.   
  12.             # In practice, we'd need some logic to check username/password   
  13.             # here, but since this is an example...   
  14.             return HttpResponse("You're logged in.")   
  15.   
  16.         # The test cookie failed, so display an error message. If this  
  17.         # was a real site we'd want to display a more friendly message.   
  18.         else:   
  19.             return HttpResponse("Please enable cookies and try again.")   
  20.   
  21.     # If we didn't post, send the test cookie along with the login form.   
  22.     request.session.set_test_cookie()   
  23.     return render_to_response('foo/login_form.html')  

注意,内建的登录和注销方法为你处理了这些

在视图外使用sessions
内部每个session只是在django.contrib.sessions.models定义的普通的Django模型,因为它是一个普通模型,你可以使用
普通的Django数据库API访问sessions:
Java代码 复制代码
  1. >>> from django.contrib.sessions.models import Session   
  2. >>> s = Session.objects.get_object(pk='2b1189a188b44ad18c35e113ac6ceead')   
  3. >>> s.expire_date   
  4. datetime.datetime(2005820133512)  

你将需要调用get_decoded()来得到准确的session数据,这是必需的,因为字典存储为一个编码的格式:
Java代码 复制代码
  1. >>> s.session_data   
  2. 'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'  
  3. >>> s.get_decoded()   
  4. {'user_id'42}  


当sessions保存时
当session修改后Django默认只保存到session数据库,即当它的字典值被赋值或删除时:
Java代码 复制代码
  1. # Session is modified.   
  2. request.session['foo'] = 'bar'  
  3.   
  4. # Session is modified.   
  5. del request.session['foo']   
  6.   
  7. # Session is modified.   
  8. request.session['foo'] = {}   
  9.   
  10. # Gotcha: Session is NOT modified, because this alters   
  11. # request.session['foo'] instead of request.session.   
  12. request.session['foo']['bar'] = 'baz'  

为了更改这个默认的行为,需要设置SESSION_SAVE_EVERY_REQUEST设置为True,如果SESSION_SAVE_EVERY_REQUEST为True
Django将在每个单独的请求保存session到数据库,设置当它没有改变时
注意只有当session被创建或修改时session cookie才被发送,如果SESSION_SAVE_EVERY_REQUEST为True,session
cookie将对每次请求发送
同样,session cookie的expires部分在每次session cookie发送时更新

浏览器长度的sessions与持久化sessions
你可能已经注意到Google发送的cookie包含expires=Sun, 17-Jan-2038 19:14:07 GMT; Cookies可以可选的包含一个过期
日期,该日期告诉浏览器什么时候删除cookie,如果一个cookie不包含过期值,浏览器将在用户关闭浏览器窗口时过期
你可以通过SESSION_EXPIRE_AT_BROWSER_CLOSE设置来控制session框架在这点上的行为
SESSION_EXPIRE_AT_BROWSER_CLOSE默认设置为False,这意味着session cookies将存储在用户的浏览器中持续
SESSION_COOKIE_AGE秒(默认为两星期,即1209600秒),如果你不想人们每次打开浏览器时都不得不登录的话可以使用它
如果SESSION_EXPIRE_AT_BROWSER_CLOSE设置为True,Django将使用浏览器长度的cookies

其它session设置
除了已经提到的设置,还有一些其它影响Django的session框架使用cookies的设置:
Setting                    Default        Explanation
SESSION_COOKIE_DOMAIN      None           session cookies使用的域名,设置它为一个字符串,如".lawrence.com"
                                          来使用跨域名的cookies,或者设置None来使用标准cookie
SESSION_COOKIE_NAME        "sessionid"    使用sessions 的cookie名,可以是任意字符串
SESSION_COOKIE_SECURE      False          session cookie是否使用"安全"cookie,如果设置为True,cookie将被
                                          标记为"安全",这意味着浏览器将保证cookie只通过HTTPS传送
技术细节
出于好奇,这里有一些关于session框架内部工作的技术注解:
1,session字典接受任意pickleable Python对象,参考Python内建的pickle模块文档得到更多关于这怎样工作的信息
2,Session数据存储在名为django_session的数据库表中
3,Session数据是"lazily":如果你从不访问request.session,Django不会接触那个数据库表
4,Django只在需要时传送cookie,如果你不设置任何session数据,它将不会发送session cookie(除非SESSION_SAVE_
EVERY_REQUEST设置为True)
5,Django的sessions框架是完整的,单独的和基于cookie的,它不像其它工具(PHP,JSP)一样求诸于把session IDs放在
URLs中
如果你仍然很好奇,源代码是非常直接的,你可以查看django.contrib.sessions

用户和认证
现在我们将浏览器和真实的人连接起来已经完成了一半,Sessions提供我们在多浏览器请求之间存储数据的一种方式,第
二个因素是使用这些sessions来让用户登录,当然,我们不能只信任用户所说的他们是谁,所以我们将需要认证它们
自然,Django提供工具来处理这个通常的任务(以及许多其它的),Django的用户认证系统处理用户,组,权限和基于
cookie的用户sessions,这个系统通常称为"认证/授权"系统,这个名字解释了用户通常分两个步骤处理:
1,验证(认证)用户是她宣称的人(通常通过对数据库检查用户名和密码)
2,验证用户授权处理一些操作(通常检查权限表)
遵循这些需要,Django的认证/授权系统由一些部分组成:
1,Users
2,Permissions:二元(yes/no)标记来指示用户是否可以处理某一任务
3,Groups:把标签和权限赋予超过一个用户的通常的方式
4,Messages:排入队列和显示用户的系统消息的简单方式
5,Profiles:用自定义域扩展用户对象的机制
如果你已经使用了admin工具(第6章),你已经看到许多这些工具,并且如果你在admin中编辑了用户或组你事实上已经在
编辑认证系统的数据库表

安装
类似于session工具,认证支持在django.contrib中绑定为Django程序,它需要安装,像session系统一样它默认已经被
安装,但是如果你删除了它,你将需要遵循这些步骤来安装它:
1,确认session框架安装了(参考上面的内容),跟踪用户显然需要cookies,并且构建在session框架之上
2,把'django.contrib.auth'放到你的INSTALLED_APPS设置中并运行manage.py syncdb
3,确认'django.contrib.auth.middleware.AuthenticationMiddleware'在你的MIDDLEWARE_CLASSES设置中,并且它在
SessionMiddleware之后
拥有了这些安装,我们已经可以在视图方法中处理用户,你将在视图中使用来访问用户的主要接口是request.user,它是
一个表示当前登录的用户的对象,如果用户没有登录,它将被替代为一个AnonymousUser对象(参考下面更多细节)
你可以使用is_authenticated()方法很轻松的分辨用户是否登录:
Java代码 复制代码
  1. if request.user.is_authenticated():   
  2.     # Do something for authenticated users.   
  3. else:   
  4.     # Do something for anonymous users.  


使用用户
一旦你拥有一个用户--通常从request.user得到,但也可能通过下面讨论的一个其它方法得到--你已经得到该对象的一些
域和方法,AnonymousUser对象仿效其中一些域和方法,但是不全,所以你应该在你确认处理的是真实的用户对象之前一
直检查user.is_authenticated()
User对象的域
Field            Description
username         必需的,30个字符或更少,只允许文字和数字字符(字母,数字和下划线)
first_name       可选,30个字符或更少
last_name        可选,30个字符或更少
email            可选,E-mail地址
password         必需的,哈希的元数据秘密(Django不存储原始密码),参看下面的"密码"部分得到更多关于这个值
is_staff         布尔值,指示用户是否可以访问admin站点
is_active        布尔值,指示用户是否可以登录,把这个标记设置为False而不是删除用户
is_superuser     布尔值,指示用户是否拥有所有的权限而不用显示的赋予它们
last_login       用户最后登录的datetime,默认设置为当前date/time
date_joined      当用户创建时的datetime,当用户创建时默认设置为当前date/time
User对象的方法
Method                   Description
is_authenticated()       对"真实的"User对象一直返回True,这是分辨用户是否认证的方式,它不暗示任何权限,并且
                         不检查用户是否active,它只指示用户成功认证
is_anonymous()           只对AnonymousUser对象返回True(对"真实"User对象返回False),通常你应该选择使用
                         is_authenticated()方法而不是这个方法
get_full_name()          返回first_name加上last_name,使用一个空格间隔
set_password(passwd)     设置用户的密码为给定的原始密码,它会处理密码哈希,这事实上不会保存User对象
check_password(passwd)   如果给定的原始密码是该用户的正确的密码则返回True,这会在比较时处理密码哈希
get_group_permissions()  从用户所属的组返回用户拥有的权限字符串的列表
get_all_permissions()    从用户所属的组和用户的权限返回用户拥有的全息字符串的列表
has_perm(perm)           如果用户拥有该特殊权限则返回True,perm的格式为"package.codename",如果用户inactive
                         该方法将一直返回False
has_perms(perm_list)     如果用户拥有这些特殊权限则返回True,如果用户为inactive,该方法将一直返回False
has_module_perms(appname)如果用户拥有给定appname的任一权限则返回True,如果用户inactive则一直返回False
get_and_delete_messages()返回用户的队列中的Message对象列表并从队列中删除消息
email_user(subj, msg)    发送一个e-mail给用户,这个email从DEFAULT_FROM_EMAIL设置发送,你也可以传递第3个参数
                         from_email来覆盖email的发送地址
get_profile()            返回站点特有的用户的轮廓,参考下面的轮廓部分得到更多关于此方法
最后,User对象由两个多对多域,groups和permissions,User对象可以像其它多对多域一样访问它们相关的对象:
Java代码 复制代码
  1. # Set a users groups:   
  2. myuser.groups = group_list   
  3.   
  4. # Add a user to some groups:   
  5. myuser.groups.add(group1, group2,...)   
  6.   
  7. # Remove a user from some groups:   
  8. myuser.groups.remove(group1, group2,...)   
  9.   
  10. # Remove a user from all groups:   
  11. myuser.groups.clear()   
  12.   
  13. # Permissions work the same way   
  14. myuser.permissions = permission_list   
  15. myuser.permissions.add(permission1, permission2, ...)   
  16. myuser.permissions.remove(permission1, permission2, ...)   
  17. myuser.permissions.clear()  


登录和注销
Django提供内建的视图方法来处理登录和注销(以及一些其它的好技巧),但现在先让我们看看怎样"手动"登录和注销用户
Django在django.contrib.auth中提供两个方法来处理这些动作:authenticate()和login()
使用authenticate()来认证给定的用户名和密码,它有两个关键字参数,username和password,并且如果密码是合法的则
它返回一个User对象,如果密码不合法,authenticate()返回None:
Java代码 复制代码
  1. >>> from django.contrib import auth authenticate   
  2. >>> user = auth.authenticate(username='john', password='secret')   
  3. >>> if user is not None:   
  4. ...     print "Correct!"  
  5. ... else:   
  6. ...     print "Oops, that's wrong!"  
  7. Oops, that's wrong!  

在视图中使用login()来登录用户,它使用一个HttpRequest对象和一个User对象并使用Django的session框架在session中
保存用户的ID
这个例子展示了你怎样在视图方法中使用authenticate()和login():
Java代码 复制代码
  1. from django.contrib import auth   
  2.   
  3. def login(request):   
  4.     username = request.POST['username']   
  5.     password = request.POST['password']   
  6.     user = auth.authenticate(username=username, password=password)   
  7.     if user is not None and user.is_active:   
  8.         # Correct password, and the user is marked "active"  
  9.         auth.login(request, user)   
  10.         # Redirect to a success page.   
  11.         return HttpResponseRedirect("/account/loggedin/")   
  12.     else:   
  13.         # Show an error page   
  14.         return HttpResponseRedirect("/account/invalid/")  

在你的视图中使用django.contrib.auth.logout()来注销登录的用户,它使用一个HttpRequest对象并且没有返回值:
Java代码 复制代码
  1. from django.contrib import auth   
  2.   
  3. def logout(request):   
  4.     auth.logout(request)   
  5.     # Redirect to a success page.   
  6.     return HttpResponseRedirect("/account/loggedout/")  

注意如果用户没有登录的话logout()不会抛出任何异常
简单方式的登录和注销
实践中,你通常不需要写你自己的登录/注销方法,auth系统带有一套视图来处理登录和注销
使用认证视图的第一步是修改你的URL配置,你将需要添加这些内容:
Java代码 复制代码
  1. from django.contrib.auth.views import login, logout   
  2.   
  3. urlpatterns = patterns('',   
  4.     # existing patterns here...   
  5.     (r'^accounts/login/$',  login)   
  6.     (r'^accounts/logout/$', logout)   
  7. )  

/accounts/login/和/accounts/logout/是Django默认为这些视图使用的URLs,但是你做出一点努力就可以把它们放在任
何你想要的位置,login视图默认渲染registration/login.html视图(你可以通过传递一个额外的视图参数template_na
me更改这个模板名),这个表单需要包含一个用户名和密码域,一个简单的模板可能看起来像这样:
Java代码 复制代码
  1. {% extends "base.html" %}   
  2.   
  3. {% block content %}   
  4.   
  5.   {% if form.errors %}   
  6.     <p class="error">Sorry, that's not a valid username or password</p>   
  7.   {% endif %}   
  8.   
  9.   <form action='.' method='post'>   
  10.     <label for="username">User name:</label>   
  11.     <input type="text" name="username" value="" id="username">   
  12.     <label for="password">Password:</label>   
  13.     <input type="password" name="password" value="" id="password">   
  14.   
  15.     <input type="submit" value="login" />   
  16.     <input type="hidden" name="next" value="{{ next }}" />   
  17.   <form action='.' method='post'>   
  18.   
  19. {% endblock %}  

如果用户成功登录,她将默认被重定向到/accounts/profile/,你可以通过提供一个叫next的在登录之后重定向的URL值
的hidden域来覆盖它,你也可以使用GET参数传递这个值到login视图,它将作为叫next的变量被自动添加到context中
注销视图工作起来有一点不同,默认它渲染registration/logged_out.html模板(它通常包含一个"你已经成功注销"的信
息),尽管如此,你可以通过一个额外参数next_page来调用视图,它将告诉视图在注销后重定向

限制登录的用户访问
当然,我们经历这些麻烦是为了使我们可以限制访问我们站点的一部分
最简单最原始的限制访问页面的方式是检查request.user.is_authenticated()并重定向到登录页面:
Java代码 复制代码
  1. from django.http import HttpResponseRedirect   
  2.   
  3. def my_view(request):   
  4.     if not request.user.is_authenticated():   
  5.         return HttpResponseRedirect('/login/?next=%s' % request.path)   
  6.     # ...  

或者显示一条出错信息:
Java代码 复制代码
  1. def my_view(request):   
  2.     if not request.user.is_au
    安徽新华电脑学校专业职业规划师为你提供更多帮助【在线咨询