之前有在用Django写一些小网站,现在暑假想说再来複习一下之前买的这本书
于是我就把它写成一系列的文章,也方便查语法
而且因为这本书大概是2014年出的,如今Django也已经出到2.多版
有些内容也变得不再支援或适用,而且语法或许也改变了
所以我会以最新版的Python和Django来修正这本书的内容跟程式码
目录:django系列文章-Django学习纪录
13. 用户的登入与登出
13.1 用户
使用django内建的用户权限系统
settings.py
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', # <-确认此行有加 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'restaurants',]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',]
如果是第一次安装记得同步资料库
python manage.py migrate
13.2 登入
13.2.1 确认使用者身分
首先设定一个登入页面
mysite/templates/login.html
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <title>登入</title> </head> <body> <form action="" method="post"> <label for="username">用户名称:</label> <input type="text" name="username" value="" id="username"> <br/> <label for="password">用户密码:</label> <input type="password" name="password" value="" id="password"> <br/> <input type="submit" value="登入" /> </form> </body></html>
views.py
...from django.contrib import authdef login(request): if request.user.is_authenticated: return HttpResponseRedirect('/index/') username = request.POST.get('username', '') password = request.POST.get('password', '') user = auth.authenticate(username=username, password=password) if user is not None and user.is_active: auth.login(request, user) return HttpResponseRedirect('/index/') else: return render(request, 'login.html', locals())...
HttpRequest物件中包含了一个user属性,代表了当前的使用者
如果用户已经登入,则HttpRequest.user是一个User物件,也就是具名用户
如果使用者尚未登入,HttpRequest.user是一个AnonymousUser物件,也就是匿名用户
User物件中的常用属性:
auth.authenticate
方法接受两个引数username、password如果帐密正确会回传具名用户的User物件如果不正确则回传None而user.is_active
确认该帐户有没有被冻结只要任何检查有问题就回到登入页面让使用者输入帐密如果检查都通过就使用auth.login
方法来登入13.2.2 登入使用者并保持其登入
auth.login需要一个HttpRequest物件和一个User物件当做引数,他会利用Django的session将这个具名用户保存在该session中,这也代表了该用户的登入状态得以跨页面的保存直到session结束或登出
Django中所谓的已经登入,代表在当下的HttpRequest中的user属性是一个具名的User物件
13.2.3 根据用户身分,确认并规範使用者的操作功能
製作首页
mysite/templates/index.html
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <title>Restaurant King</title> </head> <body> <h2>欢迎来到餐厅王</h2> {% if request.user.is_authenticated %} <p>{{request.user}} 您已经登入啰</p> <a href="/restaurants_list/">餐厅列表</a> {% else %} <p>您尚未登入喔~<a href="/accounts/login/">登入</a></p> {% endif %} </body></html>
views.py
...def index(request): return render(request, 'index.html')...
urls.py
from restaurants.views import login, indexurlpatterns = [ ... path('index/', index), path('accounts/login/', login), ...]
至于为何要使用/accounts/login/
这个pattern
因为/accounts/login/
是Django默认的登入pattern(对于登出而言,/accounts/logout/
也是默认值),这个pattern对于某些Django的函式而言是参数的预设值,使用默认的pattern可以使得我们在使用到这些函式时减少一些负担,不过如果没有其他考量的话,使用任何想要的pattern都是可以的
我们也可以在settings.py来设定此一默认值,LOGIN_URL
可以修改成任意想要的默认pattern
13.3 登出
views.py
...def logout(request): auth.logout(request) return HttpResponseRedirect('/index/')...
这里的auth.logout
方法会将用户登出
只是要注意的是这个方法用在匿名用户上也不会产生错误
index.html
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <title>Restaurant King</title> </head> <body> <h2>欢迎来到餐厅王</h2> {% if request.user.is_authenticated %} <p> {{request.user}} 您已经登入啰~ <a href="/accounts/logout/">登出</a> </p> <a href="/restaurants_list/">餐厅列表</a> {% else %} <p>您尚未登入喔~<a href="/accounts/login/">登入</a></p> {% endif %} </body></html>
urls.py
from restaurants.views import login, index, logouturlpatterns = [ ... path('index/', index), path('accounts/login/', login), path('accounts/logout/', logout), ...]
13.4 使用内建的login/logout视图
Django其实已经撰写好了登出与登入的视图函式
使用方法:
urls.py
...from django.contrib.auth import viewsurlpatterns = [ ... path('index/', index), path('accounts/login/', views.LoginView.as_view()), path('accounts/logout/', views.LogoutView.as_view()), path('accounts/profile/', index), ...]
这两个视图函式预设使用的模板分别是在
registration/login.html和registration/logged_out.html
所以把mysite/templates/login.html複製到registration资料夹中
把mysite/templates/index.html複製到registration资料夹并重新命名为logged_out.html
就可以正常使用了
13.4.2 内建login提供给呼叫模板的变量
提供给模板的主要有下列变量
... <body> <form action="{% url 'login' %}" method="post"> {% csrf_token %} <table> {{ form.as_table }} </table> <input type="submit" value="登入" /> </form> </body>...
在url pattern中加入name
这个参数
path('accounts/login/', views.LoginView.as_view(), name='login'),path('accounts/logout/', views.LogoutView.as_view(), name='logout'),
介绍一下{% url '字串' %}
的用法
django会将这里的字串对应到符合的name
参数的url pattern
例如{% url 'login' %}
即为对应到path('accounts/login/', views.LoginView.as_view())
这是一个极佳的"反查"手段避免我们将URL写死在模板里
13.4.3 提供重导URL给内建login
使用者登入之后,我们经常要帮他重导回某个特定的页面,譬如首页
内建的login预设会重导回/accounts/profile/
这就是为什么我们要加入
path('accounts/profile/', index)
如果删去这行
但是仍要让内建的login能够照我们的意思来重导
有以下几种方法:
于settings.py中设定LOGIN_REDIRECT_URL
LOGIN_REDIRECT_URL = "/index/"
透过POST方法传送next(或是其他的REDIRECT_FIELD_NAME)栏位及其值
mysite/templates/registration/login.html
... <input type="submit" value="login" /> <input type="hidden" name="next" value="{{ next }}" /> <!--利用此行--> </form>...
利用名为next的隐藏元件来传送重导URL,而这个URL是来自内建login提供的变量{{ next }}
透过GET方法传送next(或是其他的REDIRECT_FIELD_NAME)栏位及其值
直接透过在登入URL: /accounts/login/?next=/index/中附加查询字串来提供next栏位
mysite/templates/registration/login.html
...<body> <form action="{% url 'login' %}?next=/index/" method="post"> {% csrf_token %} <table> {{ form.as_table }} </table> <input type="submit" value="登入" /> </form> </body>...
不过第二种方法还是依据第一种方式的设定,只能算是解决next值传递的方式而不算是设定一个next栏位的方法
对于预设的重导行为会偏向使用第一种方式进行设定,对于重导回登入前一个页面的行为会偏向使用第三个方法
如果没有特殊需求的话,建议各位遵循使用next这个栏位名称,可以在使用上避免一些需要调整的负担,也能有较好的一致性
13.4.4 对于内建的login视图使用别的模板名称
path('accounts/login/', views.LoginView.as_view(template_name='login.html'), name='login'),path('accounts/logout/', views.LogoutView.as_view(template_name='index.html'), name='logout'),
加入template_name
这个参数便可以使用模板目录底下任意的模板了