1. 程式人生 > >django搭建一個資產管理系統2.登入系統

django搭建一個資產管理系統2.登入系統

思路:先在本機建立好專案,再移植到伺服器上,本機環境儘量與Server端環境一樣,

python3.6 django2.2.0

開發軟體 pycharm

首先建立專案sams (Server assets management system)

django-admin startproject sams
cd sams
python manage.py startapp login

檢視目錄結構

然後設定語言和時區,在setting中修改LANGUAGE_CODE和TIME_ZONE,修改為

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

然後啟用專案,成功後用瀏覽器訪問127.0.0.1:8000如下圖所示

python manage.py runserver

我們使用資料庫管理登入的賬戶密,所以我們登入到Server上

mysq -u root -p
#然後按兩下回車登入mysql


GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'password' WITH GRANT OPTION;

// %:表示從任何主機連線到mysql伺服器,password自己任意設定

FLUSH   PRIVILEGES; 
// 重新整理資料庫

CREATE DATABASE sams CHARACTER SET utf8;

show databases;
//當然你可以新建其他登入mysql的賬戶並設定許可權,此處只是為了省事兒

然後我們連線使用django連線資料庫

依賴pymysql

pip install pymysql

修改setting, 在前面加上 import pymysql

在之後在setting中找到之前DATABASES,將其註釋掉.然後指定自己server端的mysql資料庫

"""
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
"""

pymysql.install_as_MySQLdb()
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',   # 資料庫引擎
        'NAME': 'sams',  # 資料庫名,先前建立的
        'USER': 'root',     # 使用者名稱,可以自己建立使用者
        'PASSWORD': 'password',  # 密碼
        'HOST': '10.244.220.220',  # mysql服務所在的主機ip
        'PORT': '3306',         # mysql服務埠
    }
} 

至此資料庫設定已完成

然後就是資料庫模型設定

此處我只把註冊當成一個限制登入的操作,所以很多東西都可以不新增,有需要的話再舉一反三進行新增相關欄位(我只需要賬戶,密碼,有需要可以再新增一些性別,建立日期,管理員/使用者等)

進入login/models.py檔案

from django.db import models

# Create your models here.
class User(models.Model):
    name=models.CharField(max_length=128,unique=True)
    password=models.CharField(max_length=256)
    class Meta:
        ordering = ["name"]
        db_table='user'

然後註冊app(login為一個app),每新增一個app都需要在settings裡面設定

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'login',
]

app中的models建立好了後,並不會自動地在資料庫中生成相應的資料表,需要你手動建立。

進入Pycharm的terminal終端,執行下面的命令:

python manage.py makemigrations

返回結果:

D:\aworkstation\sams>python manage.py makemigrations
Migrations for 'login':
  login\migrations\0001_initial.py
    - Create model User

D:\aworkstation\sams>

Django自動為我們建立了login\migrations\0001_initial.py檔案,儲存了我們的第一次資料遷移工作,也就是建立了User模型。

接著執行下面的命令:

python manage.py migrate

Django將在資料庫內建立真實的資料表。如果是第一次執行該命令,那麼一些內建的框架,比如auth、session等的資料表也將被一同建立,如下所示:

D:\aworkstation\sams>python manage.py migrate
System check identified some issues:

WARNINGS:
?: (mysql.W002) MySQL Strict Mode is not set for database connection 'default'
        HINT: MySQL's Strict Mode fixes many data integrity problems in MySQL, such as data truncation upon insertion, by escalating warnings in
to errors. It is strongly recommended you activate it. See: https://docs.djangoproject.com/en/2.2/ref/databases/#mysql-sql-mode
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, login, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying login.0001_initial... OK
  Applying sessions.0001_initial... OK

D:\aworkstation\sams>

在mysql上就可以看到新建的表了

MariaDB [(none)]> use sams;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [sams]> show tables;
+----------------------------+
| Tables_in_sams             |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
| user                       |
+----------------------------+
11 rows in set (0.00 sec)

MariaDB [sams]> 

但此時表的內容是空的

MariaDB [sams]> select * from user;
Empty set (0.00 sec)

我們進行到下一步,使用django自帶的admin後臺管理,在admin中註冊這個模型

進入login/admin.py檔案,程式碼如下:

from django.contrib import admin

# Register your models here.

from . import models

admin.site.register(models.User)

Django的admin後臺擁有完整的較為安全的使用者認證和授權機制,防護等級還算可以。

要進入該後臺,需要建立超級管理員,該管理員和我們先前建立的User使用者不是一個概念,要注意區別對待。

同樣在Pycharm的終端中,執行下面的命令:

python manage.py createsuperuser
D:\aworkstation\sams>python manage.py createsuperuser
使用者名稱 (leave blank to use 'Administrator'): admin
電子郵件地址:
Password:
Password (again):
Superuser created successfully.

D:\aworkstation\sams>

然後執行Python manage.py runserver 就可以啟動我們的開發伺服器了,然後在瀏覽器中訪問http://127.0.0.1:8000/admin/地址

就可以輸入賬戶密碼登入了 登入後的介面如圖所示

然後我們就可以新增一個使用者了

此時在mysql中就可以看到新建的賬戶了

MariaDB [sams]> select * from user;
+----+-------+----------+
| id | name  | password |
+----+-------+----------+
|  1 | admin | password |
+----+-------+----------+
1 row in set (0.00 sec)

然後就是路由設計了

重要說明:由於本專案目的是打造一個針對管理系統、應用程式等需求下的可重用的登入/註冊app,而不是入口網站、免費部落格等無需登入即可訪問的網站,所以在url路由、跳轉策略和檔案結構的設計上都是儘量自成體系。具體訪問的策略如下:

  • 未登入人員,不論是訪問index還是login和logout,全部跳轉到login介面
  • 已登入人員,訪問login會自動跳轉到index頁面
  • 已登入人員,不允許直接訪問register頁面,需先logout
  • 登出後,自動跳轉到login介面

根據上面的策劃,開啟sams/url.py檔案,寫入下面的程式碼:

from django.contrib import admin
from django.urls import path
from login import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    path('login/', views.login),
    path('register/', views.register),
    path('logout/', views.logout),
]

此時會有報警的下劃線產生,這是因為我們還沒在login中配置views,我們下一步就是去編輯/login/views檔案

from django.shortcuts import render
from django.shortcuts import redirect

# Create your views here.


def index(request):
    pass
    return render(request, 'login/index.html')


def login(request):
    pass
    return render(request, 'login/login.html')


def register(request):
    pass
    return render(request, 'login/register.html')


def logout(request):
    pass
    return redirect("/login/")

此處只搭建了一個基礎框架,然後開始HTML介面

在專案根路徑的login目錄中建立一個templates目錄,再在templates目錄裡建立一個login目錄。這麼做有助於app複用,防止命名衝突,能更有效地組織大型工程.當然這一段是我抄的

login/templates/login目錄中建立三個檔案index.htmllogin.html以及register.html ,並寫入如下的程式碼:

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首頁</title>
</head>
<body>
<h1>這僅僅是一個主頁模擬!請根據實際情況接入正確的主頁!</h1>
</body>
</html>

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登入</title>
</head>
<body>
<h1>登入頁面</h1>
</body>
</html>

register.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>註冊</title>
</head>
<body>
<h1>註冊頁面</h1>
</body>
</html>

現在我們的專案架構如圖所示

啟動伺服器,在瀏覽器訪問http://127.0.0.1:8000/index/等頁面,如果能正常顯示,說明一切OK!

image

現在,我們整個專案的基本框架已經搭建起來了!

再之後就是修改登入介面了

修改login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登入</title>
</head>
<body>

    <div style="margin: 15% 40%;">
        <h1>歡迎登入!</h1>
       <form action="/login/" method="post">
            <p>
                <label for="id_username">使用者名稱:</label>
                <input type="text" id="id_username" name="username" placeholder="使用者名稱" autofocus required />
            </p>
            <p>
                <label for="id_password">密碼:</label>
                <input type="password" id="id_password" placeholder="密碼" name="password" required >
            </p>
            <input type="submit" value="確定">
        </form>
    </div>

</body>
</html>

再訪問就可以看到介面是這個樣子

這個介面很醜,所以我們得引入bootstrap4,前端真的是一言難盡啊,花樣太多了

修改login.html

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- 上述meta標籤*必須*放在最前面,任何其他內容都*必須*跟隨其後! -->
    <!-- Bootstrap CSS -->
    <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
    <title>登入</title>
  </head>
  <body>
    <div class="container">
            <div class="col">
              <form class="form-login" action="/login/" method="post">
                  <h3 class="text-center">歡迎登入</h3>
                  <div class="form-group">
                    <label for="id_username">使用者名稱:</label>
                    <input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
                  </div>
                  <div class="form-group">
                    <label for="id_password">密碼:</label>
                    <input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
                  </div>
                <div>
                  <a href="/register/" class="text-success "><ins>新使用者註冊</ins></a>
                  <button type="submit" class="btn btn-primary float-right">登入</button>
                </div>
              </form>
            </div>
    </div> <!-- /container -->

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    {#    以下三者的引用順序是固定的#}
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script src="https://cdn.bootcss.com/popper.js/1.15.0/umd/popper.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>

  </body>
</html>

此時介面就是這樣的

但若是內網環境(因為之前引入的js,jQuery這些都是直接引用的網上的,我們需要下載下來),載入很久之後會變成這樣

此時我們需要將引用的js,jQuery下載到本地,首先在login下新建static/login/css/,新建一個txt,重新命名為與之相同的名字,如bootstrap.min.js

然後在瀏覽器輸入https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css,全選複製,然後貼上到之前的那個檔案中.

然後再修改login.html,在開頭加上

{% load static %}

然後將<link href=修改為本地路徑

具體如下

{% load static %}
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- 上述meta標籤*必須*放在最前面,任何其他內容都*必須*跟隨其後! -->
    <!-- Bootstrap CSS -->
    <link href="{% static 'login/css/bootstrap.min.css' %}" rel="stylesheet">
    <title>登入</title>
  </head>
  <body>
    <div class="container">
            <div class="col">
              <form class="form-login" action="/login/" method="post">
                  <h3 class="text-center">歡迎登入</h3>
                  <div class="form-group">
                    <label for="id_username">使用者名稱:</label>
                    <input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
                  </div>
                  <div class="form-group">
                    <label for="id_password">密碼:</label>
                    <input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
                  </div>
                <div>
                  <a href="/register/" class="text-success "><ins>新使用者註冊</ins></a>
                  <button type="submit" class="btn btn-primary float-right">登入</button>
                </div>
              </form>
            </div>
    </div> <!-- /container -->

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    {#    以下三者的引用順序是固定的#}
    <script src="{% static 'login/css/jquery.js' %}"></script>
    <script src="{% static 'login/css/popper.js' %}"></script>
    <script src="{% static 'login/css/bootstrap.min.js' %}"></script>

  </body>
</html>

然後新增一個背景圖片

/login/static/login目錄下建立一個image目錄,css中新增我們為登入檢視寫的css檔案,這裡是login.css,image目錄中,拷貝進來你想要的背景圖片,這裡是bg.jpg。最終目錄結構如下:

下面我們修改一下login.html的程式碼,主要是引入了login.css檔案,注意最開頭的{% load static %},表示我們要載入靜態檔案。

{% load static %}
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- 上述meta標籤*必須*放在最前面,任何其他內容都*必須*跟隨其後! -->
    <!-- Bootstrap CSS -->
    <link href="{% static 'login/css/bootstrap.min.css' %}" rel="stylesheet">
      <link href="{% static 'login/css/login.css' %}" rel="stylesheet"/>
    <title>登入</title>
  </head>
  <body>
    <div class="container">
            <div class="col">
              <form class="form-login" action="/login/" method="post">
                  <h3 class="text-center">歡迎登入</h3>
                  <div class="form-group">
                    <label for="id_username">使用者名稱:</label>
                    <input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
                  </div>
                  <div class="form-group">
                    <label for="id_password">密碼:</label>
                    <input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
                  </div>
                <div>
                  <a href="/register/" class="text-success "><ins>新使用者註冊</ins></a>
                  <button type="submit" class="btn btn-primary float-right">登入</button>
                </div>
              </form>
            </div>
    </div> <!-- /container -->

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    {#    以下三者的引用順序是固定的#}
    <script src="{% static 'login/css/jquery.js' %}"></script>
    <script src="{% static 'login/css/popper.js' %}"></script>
    <script src="{% static 'login/css/bootstrap.min.js' %}"></script>

  </body>
</html>

其實就是在引用bootstrap.min.css下面加了一句

login.css和其他css在一個資料夾內,login.css如下:

body {
  height: 100%;
  background-image: url('../image/bg.jpg');
    background-repeat: no-repeat;
    background-size: cover;
}
.form-login {
  width: 100%;
  max-width: 330px;
  padding: 15px;
  margin: 0 auto;
}
.form-login{
  margin-top:80px;
  font-weight: 400;
}
.form-login .form-control {
  position: relative;
  box-sizing: border-box;
  height: auto;
  padding: 10px;
  font-size: 16px;

}
.form-login .form-control:focus {
  z-index: 2;
}
.form-login input[type="text"] {
  margin-bottom: -1px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}
.form-login input[type="password"] {
  margin-bottom: 10px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
form a{
  display: inline-block;
  margin-top:25px;
  font-size: 12px;
  line-height: 10px;
}

使用任意的一個bg.jpg即可

下面的是我的登入介面

至此,資料模型和login的前端介面都做好了,但是還有事情要做

根據我們在路由中的設計,使用者通過login.html中的表單填寫使用者名稱和密碼,並以POST的方式傳送到伺服器的/login/地址。伺服器通過login/views.py中的login()檢視函式,接收並處理這一請求。

我們可以通過下面的方法接收和處理請求:

def login(request):
    if request.method == "POST":
        username = request.POST.get('username')
        password = request.POST.get('password')
        print(username, password)
        return redirect('/index/')
    return render(request, 'login/login.html')

但此時輸入賬戶密碼會報錯

錯誤原因是CSRF驗證失敗,請求被中斷。CSRF(Cross-site request forgery)跨站請求偽造,是一種常見的網路攻擊手段,具體原理和技術內容請自行百科。Django自帶對許多常見攻擊手段的防禦機制,CSRF就是其中一種,還有XSS、SQL注入等。

解決這個問題的辦法其實在Django的Debug錯誤頁面已經給出了,我們需要在前端頁面的form表單內新增一個{% csrf_token %}標籤:

    <div class="container">
            <div class="col">
              <form class="form-login" action="/login/" method="post">
                  {% csrf_token %}
                  <h3 class="text-center">歡迎登入</h3>
                  <div class="form-group">
                    <label for="id_username">使用者名稱:</label>
                    <input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
                  </div>
                  <div class="form-group">
                    <label for="id_password">密碼:</label>

這個標籤必須放在form表單內部,但是內部的位置可以隨意。

重新重新整理login頁面,確保csrf的標籤生效,然後再次輸入內容並提交。這次就可以成功地在Pycharm開發環境中看到接收的使用者名稱和密碼,同時瀏覽器頁面也跳轉到了首頁。但是此時沒有進行任何密碼驗證的,意思是隨便輸入什麼都可以跳轉到主頁,接下來我們再設定一下密碼驗證.

下面貼出當前狀態下,/login/views.py中的全部程式碼,注意其中添加了一句from . import models,匯入我們先前編寫好的model模型。

from django.shortcuts import render
from django.shortcuts import redirect
from . import models
# Create your views here.


def index(request):
    pass
    return render(request, 'login/index.html')


def login(request):
    if request.method == "POST":
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username.strip() and password:  # 確保使用者名稱和密碼都不為空
            # 使用者名稱字符合法性驗證
            # 密碼長度驗證
            # 更多的其它驗證.....
            try:
                user = models.User.objects.get(name=username)
            except:
                return render(request, 'login/login.html')
            if user.password == password:
                return redirect('/index/')
    return render(request, 'login/login.html')


def register(request):
    pass
    return render(request, 'login/register.html')


def logout(request):
    pass
    return redirect("/login/")

上面的程式碼還缺少很重要的一部分內容,也就是錯誤提示資訊!無論是登入成功還是失敗,使用者都沒有得到任何提示資訊,這顯然是不行的。

修改一下login檢視:

def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        message = '請檢查填寫的內容!'
        if username.strip() and password:
            # 使用者名稱字符合法性驗證
            # 密碼長度驗證
            # 更多的其它驗證.....
            try:
                user = models.User.objects.get(name=username)
            except :
                message = '使用者不存在!'
                return render(request, 'login/login.html', {'message': message})

            if user.password == password:
                print(username, password)
                return redirect('/index/')
            else:
                message = '密碼不正確!'
                return render(request, 'login/login.html', {'message': message})
        else:
            return render(request, 'login/login.html', {'message': message})
    return render(request, 'login/login.html')

請仔細分析一下上面的登入和密碼驗證邏輯,以及錯誤提示的安排。

這裡增加了message變數,用於儲存提示資訊。當有錯誤資訊的時候,將錯誤資訊打包成一個字典,然後作為第三個引數提供給render方法。這個資料字典在渲染模板的時候會傳遞到模板裡供你呼叫。

為了在前端頁面顯示資訊,還需要對login.html進行修改:

<form class="form-login" action="/login/" method="post">
                  {% if message %}
                    <div class="alert alert-warning">{{ message }}</div>
                  {% endif %}
                  {% csrf_token %}
                  <h3 class="text-center">歡迎登入</h3>
                  <div class="form-group">
                    <label for="id_username">使用者名稱:</label>
                    <input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
                  </div>
                  <div class="form-group">
                    <label for="id_password">密碼:</label>
                    <input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
                  </div>
                  <div>
                  <a href="/register/" class="text-success " ><ins>新使用者註冊</ins></a>
                  <button type="submit" class="btn btn-primary float-right">登入</button>
                  </div>
                </form>

Django的模板語言{% if xxx %}{% endif %}非常類似Python的if語句,也可以新增{% else %}分句。例子中,通過判斷message變數是否為空,也就是是否有錯誤提示資訊,如果有,就顯示出來!這裡使用了Bootstrap的警示資訊類alert,你也可以自定義CSS或者JS。

好了,重啟伺服器,嘗試用錯誤的和正確的使用者名稱及密碼登入,看看頁面效果吧!下面是錯誤資訊的展示:

但是這種驗證的方式太麻煩了,這裡提供一個更簡單的方式

在專案根目錄的login資料夾下,新建一個forms.py檔案,也就是/login/forms.py,又是我們熟悉的Django組織檔案的套路,一個app一套班子!

/login/forms.py中寫入下面的程式碼,是不是有一種編寫資料model模型的既視感?

from django import forms


class UserForm(forms.Form):
    username = forms.CharField(label="使用者名稱", max_length=128)
    password = forms.CharField(label="密碼", max_length=256, widget=forms.PasswordInput)

使用了Django的表單後,就要在檢視中進行相應的修改:

# login/views.py

from django.shortcuts import render
from django.shortcuts import redirect
from . import models
from . import forms
# Create your views here.


def index(request):
    pass
    return render(request, 'login/index.html')


def login(request):
    if request.method == 'POST':
        login_form = forms.UserForm(request.POST)
        message = '請檢查填寫的內容!'
        if login_form.is_valid():
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')

            try:
                user = models.User.objects.get(name=username)
            except :
                message = '使用者不存在!'
                return render(request, 'login/login.html', locals())

            if user.password == password:
                return redirect('/index/')
            else:
                message = '密碼不正確!'
                return render(request, 'login/login.html', locals())
        else:
            return render(request, 'login/login.html', locals())

    login_form = forms.UserForm()
    return render(request, 'login/login.html', locals())


def register(request):
    pass
    return render(request, 'login/register.html')


def logout(request):
    pass
    return redirect("/login/")

Django的表單很重要的一個功能就是自動生成HTML的form表單內容。現在,我們需要修改一下原來的login.html檔案:

{% load static %}
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- 上述meta標籤*必須*放在最前面,任何其他內容都*必須*跟隨其後! -->
    <!-- Bootstrap CSS -->
    <link href="{% static 'login/css/bootstrap.min.css' %}" rel="stylesheet">
      <link href="{% static 'login/css/login.css' %}" rel="stylesheet"/>
    <title>登入</title>
  </head>
  <body>
    <div class="container">
            <div class="col">
              <form class="form-login" action="/login/" method="post">
                  {% if message %}
                    <div class="alert alert-warning">{{ message }}</div>
                  {% endif %}
                  {% csrf_token %}
                  <h3 class="text-center">歡迎登入</h3>

                   {{ login_form }}

                <div>
                  <a href="/register/" class="text-success "><ins>新使用者註冊</ins></a>
                  <button type="submit" class="btn btn-primary float-right">登入</button>
                </div>
              </form>
            </div>
    </div> <!-- /container -->

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    {#    以下三者的引用順序是固定的#}
    <script src="{% static 'login/css/jquery.js' %}"></script>
    <script src="{% static 'login/css/popper.js' %}"></script>
    <script src="{% static 'login/css/bootstrap.min.js' %}"></script>

  </body>
</html>

重新啟動伺服器,會有點亂碼

這裡我們需要手動渲染欄位,將之前的{{ login_form }}替換為

<div class="form-group">
  {{ login_form.username.label_tag }}
  {{ login_form.username}}
</div>
<div class="form-group">
  {{ login_form.password.label_tag }}
  {{ login_form.password }}
</div>

然後在form類裡新增attr屬性即可,如下所示修改login/forms.py

from django import forms


class UserForm(forms.Form):
    username = forms.CharField(label="使用者名稱", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': "Username",'autofocus': ''}))
    password = forms.CharField(label="密碼", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control','placeholder': "Password"}))

重新整理後介面就恢復正常了

接下來就是使用session了

首先,修改login/views.py中的login()檢視函式:

def login(request):
    if request.session.get('is_login', None):  # 不允許重複登入
        return redirect('/index/')
    if request.method == 'POST':
        login_form = forms.UserForm(request.POST)
        message = '請檢查填寫的內容!'
        if login_form.is_valid():
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')

            try:
                user = models.User.objects.get(name=username)
            except :
                message = '使用者不存在!'
                return render(request, 'login/login.html', locals())

            if user.password == password:
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['user_name'] = user.name
                return redirect('/index/')
            else:
                message = '密碼不正確!'
                return render(request, 'login/login.html', locals())
        else:
            return render(request, 'login/login.html', locals())

    login_form = forms.UserForm()
    return render(request, 'login/login.html', locals())

既然有了session記錄使用者登入狀態,那麼就可以完善我們的登出檢視函數了:

def logout(request):
    if not request.session.get('is_login', None):
        # 如果本來就未登入,也就沒有登出一說
        return redirect("/login/")
    request.session.flush()
    # 或者使用下面的方法
    # del request.session['is_login']
    # del request.session['user_id']
    # del request.session['user_name']
    return redirect("/login/")

然後修改index檢視函式,新增相關限制:

def index(request):
    if not request.session.get('is_login', None):
        return redirect('/login/')
    return render(request, 'login/index.html')

現在只要之前登入過,就是,在瀏覽器輸入login也是會直接跳轉到index了,至此