使用django有一个好处就是有各种各样的框架可以拿来直接使用。相比flask,django自带的框架确实要多很多。比如这一章就要介绍的用户登录。Django拥有一个内置的认证(authentication)框架用来操作用户认证(authentication),会话(sessions),权限(permissions)以及用户组。这个认证(authentication)系统包含了一些普通用户的操作视图(views),例如:登录,登出,修改密码以及重置密码
这个认证(authentication)框架位于django.contrib.auth,被其他Django的contrib包调用,我们在之前的blog引用中使用这个认证(authentication)框架并为blog创建了一个超级用户来管理站点。
我们在创建应用的时候,认证框架已经包含了,在settings.py中的MIDDLEWARE。
MIDDLEWARE=[
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
AuthenticationMiddleware:使用会话(sessions)将用户和请求(requests)进行关联
SessionMiddleware:通过请求(requests)操作当前会话(sessions)
这个认证(authentication)系统还包含了以下模型(models):
User:一个包含了基础字段的用户模型(model);这个模型(model)的主要字段有:username,password,email,first_name,last_name,is_active。
Group:一个组模型(model)用来分类用户
Permission:执行特定操作的标识
下面我们就要来创建一个登录的视图。通过执行以下的操作来登录用户
1通过提交的表单获取username和password
2将得到的用户名和密码和存储在数据库中的进行认证
3检查用户是否存在
4登录用户到网站并且开始一个认证和会话
首先创建一个登录表单,在form.py中添加如下代码:
classLoginForm(forms.Form):
username=forms.CharField()
password=forms.CharField(widget=forms.PasswordInput)
forms.PasswordInput是在密码输入的HTML界面进行渲染的。接着在views.py中添加登录处理的代码
defuser_login(request):
ifrequest.method=='POST':
form=LoginForm(request.POST)
ifform.is_valid():
cd=form.cleaned_data
user=authenticate(username=cd['username'],password=cd['password'])
ifuserisnotNone:
ifuser.is_active:
login(request,user)
returnHttpResponse(u'登录成功')
else:
returnHttpResponse(u'登录失败')
else:
returnHttpResponse(u'非法登录,请注册用户')
else:
form=LoginForm()
returnrender(request,'blog/login.html',{'form':form})
1通过使用form=LoginForm(request.POST)得到提交的实例化表单
2form.is_valid()检查这个表单是否正确,如果正确,通过form.cleaned_data
取出登录的用户名和密码
3如果用户是有效的,通过user=authenticate(username=cd['username'],password=cd['password'])
对获取到的用户名和密码进行认证
4如果用户可用,则用login(request,user)方法集合用户到会话中然后返回一条成功的消息
接下来就是创建url了。在mysite的urls.py中添加url(r'^account/',include('blog.urls')),
urlpatterns=[
url(r'^admin/',admin.site.urls),
url(r'^blog/',include('blog.urls',namespace='blog',app_name='blog')),
url(r'^test/$',views.test),
url(r'^account/',include('blog.urls')),
]
在blog应用下的urls.py中添加url(r'^login/$',views.user_login)
urlpatterns=[
#postviews
url(r'^$',views.post_list_page,name='post_list'),
url(r'^(?P<post_id>d+)/share/$',views.post_share,name='post_share'),
url(r'^(?P<post_id>d+)/comment/$',views.post_comment,name='post_comment'),
url(r'^test/$',views.test),
url(r'^login/$',views.user_login)
]
最后来看我们的登录模板,在templates/blog下新建一个login.html
{%extends"base.html"%}
{%blocktitle%}登录{%endblock%}
{%blockcontent%}
<h1>用户登录</h1>
<p>请输入用户名和密码:</p>
<formaction="."method="post">
{{form.as_p}}
{%csrf_token%}
<p><inputtype="submit"value="Log-in"></p>
</form>
{%endblock%}
现在视图,url,模板都已经具备了,下面我们就是要创建一个真实的用户了。运行服务器进入admin界面进行用户名注册:用超级用户名进入后在Users点击add
输入用户名和帐号
权限设置
这里注意一点,如果忘记了普通用户的用户名和密码,可以登录超级用户帐号进去修改,但是如果连超级用户的密码也忘记了,可以通过如下的方式来修改:
在pycharm中进入Pythonconsole
输入如下代码操作用户数据库:
fromdjango.contrib.auth.modelsimportUser
user=User.objects.get(username='用户名')
user.set_password('密码‘)
user.save()
下面我们就来开始登录,在浏览器中输入:http://127.0.0.1:8000/account/login/
得到如下的界面
输入用户名和密码后提示登录成功。
django还提供以下视图(views)来处理认证(authentication):
- login:操作表单(form)中的登录然后登录一个用户
- logout:登出一个用户
- logout_then_login:登出一个用户然后重定向这个用户到登录页面
Django提供以下视图(views)来操作密码修改:
- password_change:操作一个表单(form)来修改用户密码
- password_change_done:当用户成功修改他的密码后提供一个成功提示页面
Django还包含了以下视图(views)允许用户重置他们的密码:
- password_reset:允许用户重置他的密码。它会生成一条带有一个token的一次性使用链接然后发送到用户的邮箱中。
- password_reset_done:告知用户已经发送了一封可以用来重置密码的邮件到他的邮箱中。
- password_reset_complete:当用户重置完成他的密码后提供一个成功提示页面。
在blog应用中的urls.py中添加如下代码:
fromdjango.contrib.auth.viewsimportlogin
fromdjango.contrib.auth.viewsimportlogout
fromdjango.contrib.auth.viewsimportlogout_then_login
fromdjango.contrib.auth.viewsimportpassword_change
fromdjango.contrib.auth.viewsimportpassword_change_done
fromdjango.contrib.auth.viewsimportpassword_reset
fromdjango.contrib.auth.viewsimportpassword_reset_done
fromdjango.contrib.auth.viewsimportpassword_reset_confirm
fromdjango.contrib.auth.viewsimportpassword_reset_complete
然后在urlpatterns中添加对应的路径处理函数
urlpatterns=[
#postviews
url(r'^$',views.post_list_page,name='post_list'),
url(r'^(?P<post_id>d+)/share/$',views.post_share,name='post_share'),
url(r'^(?P<post_id>d+)/comment/$',views.post_comment,name='post_comment'),
url(r'^test/$',views.test),
url(r'^login/$',login),
url(r'^logout/$',logout,name='logout'),
url(r'^logout-then-login/$',logout_then_login,name='logout_then_login'),
url(r'^password-change/$',password_change,name='password_change'),
url(r'^password-change/done/$',password_change_done,name='password_change_done'),
url(r'^password-reset/$',
password_reset,
name='password_reset'),
url(r'^password-reset/done/$',
password_reset_done,
name='password_reset_done'),
url(r'^password-reset/confirm/(?P<uidb64>[-w]+)/(?P<token>[-w]+)/$',
password_reset_confirm,
name='password_reset_confirm'),
url(r'^password-reset/complete/$',
password_reset_complete,
name='password_reset_complete'),
]
比如输入http://127.0.0.1:8000/account/password-change/则会自动跳转到密码修改的界面:
那么现在我们来优化下我们的登陆功能,前面在登陆完成以后跳转到一个页面提示跳转成功。现在我们来优化下这个功能,登陆后跳转到登陆前的界面
首先在url中
我们将url(r'^login/$',views.user_login)替换成url(r'^login/$',login,name="login")
也就是我们使用django中的登陆认证模块,使用这种认证模块必须在templates目录下创建一个新的目录命名为registration。这个路径是Django认证(authentication)视图(view)期望你的认证(authentication)模块(template)默认的存放路径。如果没有这个路径的话会提示如下的错误:
在这个新目录中创建一个新的文件,命名为login.html,然后添加如下代码:
{%extends"blog/base.html"%}
{%blocktitle%}Log-in{%endblock%}
{%blockcontent%}
<h1>用户登录</h1>
{%ifform.errors%}
<p>
用户或密码不匹配,请输入正确的用户名和密码.
</p>
{%else%}
<p>填入下面的信息登陆:</p>
{%endif%}
<divclass="login-form">
<formaction="{%url'login'%}"method="post">
{{form.as_p}}
{%csrf_token%}
<inputtype="hidden"name="next"value="{{next}}"/>
<p><inputtype="submit"value="Log-in"></p>
</form>
</div>
{%endblock%}
我们添加了一个隐藏的HTML<input>元素来提交叫做next的变量值。当你在请求(request)中传递一个next参数(举个例子:http://127.0.0.1:8000/account/login/?next=/account/),这个变量是登录视图(view)首个设置的参数。
next参数必须是一个URL。当这个参数被给予的时候,Django登录视图(view)将会在用户登录完成后重定向到给予的URL。也就是在登陆完成后URL将从http://127.0.0.1:8000/account/login/?next=/account/跳转到http://127.0.0.1:8000/account
在settings.py中添加如下配置。LOGIN_URL和LOGOUT_URL中
fromdjango.core.urlresolversimportreverse_lazy
LOGIN_URL=reverse_lazy('login')
LOGOUT_URL=reverse_lazy('logout')
这些设置的意义:
LOGIN_URL:重定向用户登录的URL(例如:使用login_required装饰器(decorator))。
LOGOUT_URL:重定向用户登出的URL。
我们使用reverse_lazy()来通过它们的名字动态构建URL。reverse_lazy()方法就像reverse()所做的一样reversesURLs,但是你可以通过使用这种方式在你项目的URL配置被读取之前进行reverseURLs。
我们现在来测试下,在post_list_page中使用login_required进行修饰
@login_required
defpost_list_page(request):
object_list=Post.objects.all()
paginator=Paginator(object_list,1)#3postsineachpage
page=request.GET.get('page')
new_comment=None
注意:这里的使用方式比较灵活。我们也可以在login_required中进行登录跳转界面的设置,这里设置了的话那么在settings.py中就不用设置了LOGIN_URL
@login_required(login_url='/account/login')
defpost_list_page(request):
object_list=Post.objects.all()
paginator=Paginator(object_list,1)#3postsineachpage
page=request.GET.get('page')
new_comment=None
下面我来进行测试,在浏览器中输入http://127.0.0.1:8000/blog。由于没有登录所以自动会跳转到http://127.0.0.1:8000/account/login/?next=/blog/进行用户登陆。这里并且传递了next的参数
输入用户名和密码后重新回到了http://127.0.0.1:8000/blog的地址并且显示了博客内容
既然已经实现了登陆,那么下面来看下退出登陆。首先在registration中创建一个logged_out.html。注意这个名称必须是logged_out,否则在退出登录的时候会自动跳转到admin/logout也就是管理页面的登出界面。文件代码如下:
{%extends"blog/base.html"%}
{%blocktitle%}Loggedout{%endblock%}
{%blockcontent%}
<h1>用户退出</h1>
<p>你已经退出登陆<ahref="{%url"login"%}">重新登陆</a></p>
{%endblock%}
在我们的博客显示界面添加退出登陆的链接:
<spanclass="user">
{%ifrequest.user.is_authenticated%}
你好{{request.user.first_name}},
<ahref="{%url"logout"%}">退出</a>
{%endif%}
</span>
页面显示如下
点击退出跳转到退出登陆的界面。我们点击重新登陆
我们点击重新登陆并再次输入用户名和密码却提示出错:
原因在于,我们在点重新登陆的时候,页面跳转到http://127.0.0.1:8000/account/login/。请注意,这个URL并没有next参数,也就是说只是纯粹的一个登录界面,在login.html中由于无法获取到next参数的值因此无法回到退出前的界面。此时url变为http://127.0.0.1:8000/accounts/profile/。我们在url中并没有这个路由,因此报错
那么如何解决呢,方法是在settings.py中设置登陆后的跳转界面
LOGIN_REDIRECT_URL=reverse_lazy('list')
并且在应用的urls.py中添加了路由
url(r'^$',views.post_list_page,name='list'),
这样在重新登陆后,界面将跳转到list这个url。这样我们再重新登录的时候就不会有问题了
在django的模块中还有修改密码,重置密码等功能,用法都是差不多的。这里简单介绍下
修改密码:
registration/目录下添加一个新的文件命名为password_form.html。添加如下代码:
{%extends"/blog/base.html"%}{%blocktitle%}Changeyoupassword{%endblock%}{%blockcontent%}
<h1>Changeyoupassword</h1>
<p>Usetheformbelowtochangeyourpassword.</p>
<formaction="."method="post">
{{form.as_p}}
<p><inputtype="submit"value="Change"></p>
{%csrf_token%}
</form>{%endblock%}
在相同的目录下创建另一个文件,命名为password_change_done.html来显示密码修改成功,为它添加如下代码:
{%extends"blog/base.html"%}{%blocktitle%}Passwordchanged{%endblock%}{%blockcontent%}
<h1>Passwordchanged</h1>
<p>Yourpasswordhasbeensuccessfullychanged.</p>
{%endblock%}