上一節,我們基本了解了 Django 的一些配置,這一節,我們將通過一個簡單的登錄頁面,進一步學習 Django 的使用。
新建項目
首先,新建一個 Django 項目,記得別弄錯了哦。
settings 中靜態文件、templates 路徑配置
這里我們已經寫好了登錄頁面(點我下載 密碼:abh5),但是這一個登錄頁面不單只是一個 HTML 文件,還有一些靜態文件。我們上一次已經說過了,想要在 HTML 中引進靜態文件的話,需要在 settings 中配置靜態文件夾。login.html 放在 templates 目錄下,也需要在 settings 中配置 templates 文件夾。配置完成后如下圖。

""" Django settings for mysite project. Generated by 'django-admin startproject' using Django 2.0.5. For more information on this file, see https://docs.djangoproject.com/en/2.0/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/2.0/ref/settings/ """ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '-3+5fklj6_ard3d!^66n9=a^6p!v381pvm6r6h=c#84w%nzm1f' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'mysite.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'mysite.wsgi.application' # Database # https://docs.djangoproject.com/en/2.0/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } # Password validation # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ]
邏輯及 url 規划
我們想要瀏覽器能夠找到這一個頁面,首先要給它配置一個 url。
urls.py 中放的是函數與路徑的對應關系。但是函數不應該放在 urls.py 中,應該新建一個 views.py ,專門用來寫函數邏輯。
這里,我們在 views.py 中定義一個 login 函數,用來收到請求后返回 login.html 頁面。要返回一個 HTML 頁面,我們就可以選擇導入 render 模塊,render 模塊可以方便地返回瀏覽器請求,HTML 頁面,還有我們想要返回的參數。

from django.shortcuts import render def login(request): return render(request, 'login.html')
寫好邏輯處理函數之后,我們就來寫函數與路徑的對應關系,也就是 url 。

from django.conf.urls import url from .views import login urlpatterns = [ url(r'^login/$', login), ]
此時的項目目錄應該如下圖。
登錄頁面配置
我們在 login.html 中通過一個 form 表單,完成用戶賬號密碼的輸入和傳送。
form 表單 action 屬性表示輸入往哪里送,往哪個 url 送。method 屬性是方法,一般都設置成 post。
這里,我們先設置 action="/baobao/" method="post",也就是專門寫一個名為 baobao 的函數,用以獲取用戶提交的數據,做是否登錄成功的判斷。
在 baobao 函數中,我們打印了瀏覽器 form post 過來的數據,然后返回一個 'O98K' 到頁面顯示 (#^.^#)

from django.conf.urls import url from .views import login, baobao urlpatterns = [ url(r'^login/$', login), url(r'^baobao/$', baobao), ]

from django.shortcuts import render, HttpResponse def login(request): return render(request, 'login.html') def baobao(request): # 獲取用戶提交的數據,做是否登錄成功的判斷 email = request.POST print(request.POST) return HttpResponse('O98K')

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/fontawesome/css/font-awesome.min.css"> <title>mysite-登錄頁面</title> <style> body { background-color: #eee; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-4 col-md-offset-4" style="margin-top: 100px"> <h1 class="text-center">請登錄</h1> <form class="form-horizontal" action="/login/" method="post"> <div class="form-group"> <label for="inputEmail3" class="col-sm-2 control-label"></label> <div class="input-group col-sm-8"> <span class="input-group-addon"><i class="fa fa-envelope-o fa-fw"></i></span> <input type="email" name="email" class="form-control" id="inputEmail3" placeholder="Email"> </div> </div> <div class="form-group"> <label for="inputPassword3" class="col-sm-2 control-label"></label> <div class="input-group col-sm-8"> <span class="input-group-addon"><i class="fa fa-key fa-fw"></i></span> <input type="password" name="pwd" class="form-control" id="inputPassword3" placeholder="Password"> </div> </div> <div class="form-group"> <div class="input-group col-sm-offset-2 col-sm-8"> <div class="checkbox"> <label> <input type="checkbox"> 記住我 </label> </div> </div> </div> <div class="form-group"> <div class="input-group col-sm-offset-2 col-sm-8"> <button type="submit" class="btn btn-primary btn-block">登錄</button> </div> </div> </form> </div> </div> </div> </body> </html>
啟動項目,在瀏覽器輸入 http://127.0.0.1:8000/login/,回車,順利出現了我們的登錄頁面。
一臉興奮,輸入隨便一個郵箱、密碼,點擊登錄。什么?怎么提示錯誤了!!!
你確定你輸入的是郵箱地址嗎(*/ω\*).....
這一個提示,是 bootstrap 前端框架幫我們做的,在前端就進行了一次簡單的表單驗證。boostrap 是什么?你現在姑且把它當成是一個前端框架,我們以后再講。
初涉 CSRF
再一次,輸入正確的郵箱地址,密碼,點擊登錄。Forbidden?這是什么鬼???
可以看到,url 已經成功跳轉到 baobao 了,不過頁面卻顯示 Forbidden。下面還有一行 CSRF verification failed. Request aborted. 這一個是 Django 自帶的 CSRF 防范機制。CSRF 是什么?某度百科有言:“CSRF(Cross-site request forgery)跨站請求偽造,也被稱為“One Click Attack”或者Session Riding,通常縮寫為CSRF或者XSRF,是一種對網站的惡意利用。”(可以看出,我們的 Django 框架,可是相當的嚴謹和方便。)
那么,這里我們應該怎么做呢?我們只需要在 form 表單標簽內部加上一句 {% csrf_token %} 即可。
此時的 login.html 應為

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/fontawesome/css/font-awesome.min.css"> <title>mysite-登錄頁面</title> <style> body { background-color: #eee; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-4 col-md-offset-4" style="margin-top: 100px"> <h1 class="text-center">請登錄</h1> <form class="form-horizontal" action="/login/" method="post"> <div class="form-group"> <label for="inputEmail3" class="col-sm-2 control-label"></label> <div class="input-group col-sm-8"> <span class="input-group-addon"><i class="fa fa-envelope-o fa-fw"></i></span> <input type="email" name="email" class="form-control" id="inputEmail3" placeholder="Email"> </div> </div> <div class="form-group"> <label for="inputPassword3" class="col-sm-2 control-label"></label> <div class="input-group col-sm-8"> <span class="input-group-addon"><i class="fa fa-key fa-fw"></i></span> <input type="password" name="pwd" class="form-control" id="inputPassword3" placeholder="Password"> </div> </div> <div class="form-group"> <div class="input-group col-sm-offset-2 col-sm-8"> <div class="checkbox"> <label> <input type="checkbox"> 記住我 </label> </div> </div> </div> <div class="form-group"> <div class="input-group col-sm-offset-2 col-sm-8"> <button type="submit" class="btn btn-primary btn-block">登錄</button> </div> </div> {% csrf_token %} </form> </div> </div> </div> </body> </html>
重啟項目,再次回到 login 頁面,輸入,登錄。終於看到我們想要的 ‘O98K’ 了。 o(╥﹏╥)o
提取數據及賬號密碼驗證
而且可以在 pycharm 的 Terminal 中看到打印出來的數據。(我是在 pycharm 的 Terminal 中啟動項目的,所以可以在這里看到打印的數據)
'email' 對應的是我們輸入的郵箱。'pwd' 對應的是我們輸入的密碼。'email' 和 'pwd' 是在哪里定義的呢?是我們在 login.html 的 form 的 input 中定義的 name 屬性。要想給服務器傳數據,必須給它們起一個 name。
既然可以獲取到瀏覽器傳過來的數據了,我們就可以在 baobao 函數中取出這些數據,做郵箱、密碼的驗證。

from django.shortcuts import render, HttpResponse def login(request): return render(request, 'login.html') def baobao(request): # 獲取用戶提交的數據,做是否登錄成功的判斷分別 # 取出郵箱和密碼進行驗證 email = request.POST.get('email', None) pwd = request.POST.get('pwd', None) if email == '123@qq.com' and pwd == 'abc123': # 登錄成功 return HttpResponse('登錄成功') else: # 登錄失敗 return HttpResponse('登錄失敗')
輸入錯誤的郵箱和密碼進行登錄,再輸入正確的郵箱和密碼進行登錄。可以看到,我們想要的邏輯都能夠正確運行。
邏輯及 url 優化
整理一下邏輯
首先,啟動項目之后,在瀏覽器輸入 http://127.0.0.1:8000/login/,瀏覽器就會給服務器發送一個 GET 請求,服務器給瀏覽器返回 login 頁面。當用戶填完登錄表單點擊登錄的時候,瀏覽器就會向服務器發送一個 POST 請求,給服務器提交數據。服務器收到瀏覽器提交過來的數據(郵箱、密碼),在邏輯函數中進行驗證,並給瀏覽器返回相應的結果。
到這里,不知道你是否發現,我們的邏輯函數和 url 設置太過累贅。明明是一個登錄功能,卻寫了兩個頁面,用兩個函數進行處理。下面,我們將對代碼進行改進。把返回 login 頁面和 郵箱密碼驗證 功能都寫進 login 函數中。

from django.shortcuts import render, HttpResponse def login(request): if request.method == 'POST': # 獲取用戶提交的數據,做是否登錄成功的判斷分別 # 取出郵箱和密碼進行驗證 email = request.POST.get('email', None) pwd = request.POST.get('pwd', None) if email == '123@qq.com' and pwd == 'abc123': # 登錄成功 return HttpResponse('登錄成功') else: # 登錄失敗 return HttpResponse('登錄失敗') # 如果不是POST,就返回login頁面 return render(request, 'login.html')
相應的,login.html 中 form 的 action 也要改變成 login,urls.py 和 views.py 中的 baobao 相關可以刪除。
登錄測試,發現可以正常運行,而且 url 一直都是 login。
頁面優化
什么?你說這樣太丑了???
對,這樣的登錄功能實在是太丑了,一般的登錄失敗之后都不會跳轉到另一個頁面說你登錄失敗,而是在輸入郵箱和密碼的頁面就提示登錄失敗了。
好的,那我們就動手改一下吧。要想錯誤信息顯示在登錄界面,就是說需要后端返回一個值到前端來。我們在 login 函數里面整理邏輯,在函數一開始的時候,就先給它一個空的錯誤信息,如果瀏覽器傳過來的是 POST 方法,並且賬號密碼不正確,那么錯誤信息 error_msg 就是 ‘賬號或密碼錯誤’。然后再通過 render 模塊返回瀏覽器。

from django.shortcuts import render, HttpResponse def login(request): error_msg = '' if request.method == 'POST': # 獲取用戶提交的數據,做是否登錄成功的判斷分別 # 取出郵箱和密碼進行驗證 email = request.POST.get('email', None) pwd = request.POST.get('pwd', None) if email == '123@qq.com' and pwd == 'abc123': # 登錄成功 return HttpResponse('登錄成功') else: # 登錄失敗 error_msg = '賬號或密碼錯誤' # 如果不是POST,就返回login頁面 return render(request, 'login.html', {'error': error_msg})
同時,login.html 也需要提取后端傳過來的 error_msg ,並顯示在登錄頁面上,相應的代碼如下。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/fontawesome/css/font-awesome.min.css"> <title>mysite-登錄頁面</title> <style> body { background-color: #eee; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-4 col-md-offset-4" style="margin-top: 100px"> <h1 class="text-center">請登錄</h1> <form class="form-horizontal" action="/login/" method="post"> <div class="form-group"> <label for="inputEmail3" class="col-sm-2 control-label"></label> <div class="input-group col-sm-8"> <span class="input-group-addon"><i class="fa fa-envelope-o fa-fw"></i></span> <input type="email" name="email" class="form-control" id="inputEmail3" placeholder="Email"> </div> </div> <div class="form-group"> <label for="inputPassword3" class="col-sm-2 control-label"></label> <div class="input-group col-sm-8"> <span class="input-group-addon"><i class="fa fa-key fa-fw"></i></span> <input type="password" name="pwd" class="form-control" id="inputPassword3" placeholder="Password"> </div> </div> <div class="form-group"> <div class="input-group col-sm-offset-2 col-sm-8"> <div class="checkbox"> <label> <input type="checkbox"> 記住我 </label> </div> </div> </div> <div class="form-group"> <div class="input-group col-sm-offset-2 col-sm-8"> <button type="submit" class="btn btn-primary btn-block">登錄</button> </div> <p class="text-danger text-center">{{ error }}</p> </div> {% csrf_token %} </form> </div> </div> </div> </body> </html>
測試登錄功能,一切正常,登陸成功跳轉頁面並顯示“登錄成功”,登錄失敗直接在表單下顯示錯誤信息。
初涉模板語言
你可能會疑惑,剛才我們在 login.html 中加的一句 {{ error }} 是什么意思啊?沒見過啊?
這個,是 Django 自帶的模板語言。不知道你是否還記得,在之前講動態頁面的時候,用 “@@xx@@”作為占位符,並在后端通過字符串替換的方法,把“@@xx@@”換成了當前時間。同理,這里的 {{ error }} 也類似於一個占位符,不過這里就不需要我們寫函數去更改了,只要我們把同名的參與以字典的形式傳給前端頁面,Django 就會自動幫我們替換。
當然,你也可以在登錄成功之后,跳轉到其他的網頁去,例如你自己的博客。
這里就會用到一個新的函數 重定向 redirect。redirect 的作用,回復一個特殊的響應,這個響應會讓用戶瀏覽器請求指定的URL。

from django.shortcuts import render, HttpResponse, redirect def login(request): error_msg = '' if request.method == 'POST': # 獲取用戶提交的數據,做是否登錄成功的判斷分別 # 取出郵箱和密碼進行驗證 email = request.POST.get('email', None) pwd = request.POST.get('pwd', None) if email == '123@qq.com' and pwd == 'abc123': # 登錄成功 # 回復一個特殊的響應,這個響應會讓用戶瀏覽器請求指定的URL return redirect('http://www.cnblogs.com/chuangming/') else: # 登錄失敗 error_msg = '賬號或密碼錯誤' # 如果不是POST,就返回login頁面 return render(request, 'login.html', {'error': error_msg})
小結一下
這一次,我們回顧了項目的新建、settings 中靜態文件、templates 路徑配置,對項目邏輯和 url 進行規划和優化,接觸到了 form 表單的使用,並且初次涉獵到 CSRF 和 Django 的模板語言,還有重定義函數 redirect。
下節預告
你會發現,我們現在只弄了一個登錄界面,就搞了這么多的邏輯,但是一個項目不是僅僅由一個登錄頁面構成的,它還會有其他的功能,登錄只是它的一小部分。那么這樣子的話,我們需要對其功能進行划分,分成一個個 APP,這樣更便於管理。至於什么是 APP,我們下一節再講,謝謝大家!(#^.^#)