一步一步理解 python web 框架,纔不會從入門到放棄 -- 簡單登陸頁面

上一節,咱們基本瞭解了 Django 的一些配置,這一節,咱們將經過一個簡單的登陸頁面,進一步學習 Django 的使用。css

新建項目

首先,新建一個 Django 項目,記得別弄錯了哦。html

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'),
]
settings.py

邏輯及 url 規劃

咱們想要瀏覽器可以找到這一個頁面,首先要給它配置一個 url。sql

urls.py 中放的是函數與路徑的對應關係。可是函數不該該放在 urls.py 中,應該新建一個 views.py ,專門用來寫函數邏輯。django

這裏,咱們在 views.py 中定義一個 login 函數,用來收到請求後返回 login.html 頁面。要返回一個 HTML  頁面,咱們就能夠選擇導入 render 模塊,render 模塊能夠方便地返回瀏覽器請求,HTML 頁面,還有咱們想要返回的參數。bootstrap

from django.shortcuts import render


def login(request):
    return render(request, 'login.html')
views.py

寫好邏輯處理函數以後,咱們就來寫函數與路徑的對應關係,也就是 url 。c#

from django.conf.urls import url

from .views import login

urlpatterns = [
    url(r'^login/$', login),
]
urls.py

此時的項目目錄應該以下圖。後端

登陸頁面配置

咱們在 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),
]
urls.py
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')
views.py
<!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>
login.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.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('登陸失敗')
views.py

輸入錯誤的郵箱和密碼進行登陸,再輸入正確的郵箱和密碼進行登陸。能夠看到,咱們想要的邏輯都可以正確運行。

邏輯及 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')
views.py

相應的,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})
views.py

同時,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

測試登陸功能,一切正常,登錄成功跳轉頁面並顯示「登陸成功」,登陸失敗直接在表單下顯示錯誤信息。

初涉模板語言

你可能會疑惑,剛纔咱們在 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})
views.py

小結一下

這一次,咱們回顧了項目的新建、settings 中靜態文件、templates 路徑配置,對項目邏輯和 url 進行規劃和優化,接觸到了 form 表單的使用,而且初次涉獵到 CSRF 和 Django 的模板語言,還有重定義函數 redirect。

下節預告

你會發現,咱們如今只弄了一個登陸界面,就搞了這麼多的邏輯,可是一個項目不是僅僅由一個登陸頁面構成的,它還會有其餘的功能,登陸只是它的一小部分。那麼這樣子的話,咱們須要對其功能進行劃分,分紅一個個 APP,這樣更便於管理。至於什麼是 APP,咱們下一節再講,謝謝你們!(#^.^#)

相關文章
相關標籤/搜索