帳號管理
這個專案是要讓教師與學生在線上進行互動,大部份的操作都需要取得使用者的身分,再決定要如何處理。所以這邊先處理使用者帳號。
建立專案與應用程式
開啟終端機或命令提示字元程式,以下面指令建立一個名為 eclassroom
的新專案:
django-admin.py startproject eclassroom
指令執行完成後,會在當前的工作資料夾產生一個名為 eclassroom
的資料夾,接下來將工作目錄切換至方才建立的 eclassroom
專案資料夾,然後在專案下新增應用程式 user
:
cd eclassroom
python manage.py startapp user
接著修改專案設定檔,將它加入專案的應用程式中。請修改 eclassroom/settings.py
,修改第 28, 58, 107, 109 行,新增第 40, 122 - 125 行:
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user',
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['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',
],
},
},
]
LANGUAGE_CODE = 'zh-hant'
TIME_ZONE = 'Asia/Taipei'
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
LOGIN_URL = '/user/login/'
LOGIN_REDIRECT_URL = '/'
在專案中會使用到自訂 CSS 與 Javascript 檔案,所以要透過第 122 行指定靜態檔案的儲存位置。
使用者登入與登出
執行以下指令建立資料庫,並建立管理員帳號:
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
接著開啟網站服務:
python manage.py runserver 0.0.0.0:80
建立路徑規則
開啟專案路徑規則檔 eclassroom/urls.py
,修改第 17 行,並新增第 18, 22 - 29 行:
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('',
TemplateView.as_view(
template_name='home.html',
extra_context={'title': '歡迎光臨'}
),
name='home'
),
path('user/', include('user.urls')),
]
我們將使用者管理相關的功能都集中到 user
這個自訂的應用程式來實作,先新增該應用程式的路徑規則檔 user/urls.py
,內容如下:
from django.urls import path
from .views import *
urlpatterns = [
path('', include('django.contrib.auth.urls')),
]
登入、登出,以及個人密碼變更等功能不需要在 views.py
裡自行實作,因為 Django 內建的應用程式 django.contrib.auth
已經有現成的功能可以直接使用,所以這邊直接將 django.contrib.auth.urls
定義的路徑規則引用進來即可。
頁面範本
請先在專案資料夾內建立 templates
資料夾,用來集中存放專案會使用到的頁面範本,專案會使用到自訂的 CSS 與 Javascript 檔案等靜態資源,也請在專案資料夾內建立 static
資料夾來存放靜態檔案,另外在 static
資料夾內,分別建立 css
與 js
資料夾以便將 CSS 樣式表與 Javascript 程式檔分開放置。
至目前為止,專案資料夾的結構大致如下:
.
├── eclassroom/
├── static/
│ ├── css/
│ └── js/
├── templates/
└── user/
網站基底範本
如果覺得 Bootstrap 的原始配色看得有點膩了,又不曉得怎麼自己調整配色的話,可以上 Bootswatch(https://bootswatch.com/) 網站挑一個別人配好的順眼樣式來替換 Bootstrap 原始的設定:
這裡以 Minty 為例,點按該卡片下方的「DOWNLOAD」按鈕後會下載一個 bootstrap.min.css
的檔案,將其複製到專案的 static/css/
資料夾內。
新增網站基底範本 templates/base.html
,內容如下:
{% load static %}<!DOCTYPE html>
<html lang="zh-hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css">
<link rel="stylesheet" href="{% static 'css/custom.css' %}">
<title>線上教室</title>
</head>
<body>
{% include "navbar.html" %}
<div class="container my-3">
{% if messages %}
<section id="messages">
<ul class="messages pl-0">
{% for msg in messages %}
<li class="alert alert-{{ msg.tags }}">{{ msg }}</li>
{% endfor %}
</ul>
</section>
{% endif %}
<section id="main-content">
{% if title %}
<h1 class="title">{{ title }}</h1>
{% endif %}
{% block content %}{% endblock %}
</section>
</div>
<footer class="border-top p-1">
<div class="container">
<small class="text-muted">Copyright 2020 線上教室</small>
</div>
</footer>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="{% static 'js/custom.js' %}"></script>
{% block custom_scripts %}{% endblock %}
</body>
</html>
新增自訂的 Javascript 程式檔 static/js/custom.js
,在裡面撰寫自訂的工具函式:
function add_css_class(filter, ...classes) {
document.querySelectorAll(filter).forEach(function(item) {
item.classList.add(...classes);
});
}
add_css_class('.alert-error', 'alert-danger');
新增自訂的 CSS 樣式檔 static/css/custom.css
,填入後面會用到的自訂 CSS 規則組:
#work-detail input[name="score"] {
width: 100px;
}
#work-list .work {
padding: .75rem 1.25rem;
margin: .25rem 0;
border: 1px solid #eee;
border-left: .25rem solid #eee;
border-radius: .25rem;
}
#work-list .work-memo {
margin-top: .5rem;
}
#work-list .work-meta {
display: flex;
justify-content: space-between;
}
#work-list .work-score {
font-size: 200%;
color: #FF7851;
}
.bd-callout-warning {
border-left-color: #f0ad4e;
}
.bd-callout-info {
border-left-color: #5bc0de;
}
.bd-callout-danger {
border-left-color: #FF7851;
}
#work-detail .score {
margin: 5pt;
font-size: 20pt;
color: red;
border-bottom: 3px double red;
text-align: right;
}
#work-list .score {
color: red;
border-bottom: 3px double red;
}
接著新增導覽列範本 templates/navbar.html
:
<nav class="navbar navbar-expand-md navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/">線上教室</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarMenu" aria-controls="navbarMenu" aria-expanded="false" aria-label="Toggle navigation">
<i class="navbar-toggler-icon"></i>
</button>
<div class="collapse navbar-collapse" id="navbarMenu">
<ul class="navbar-nav">
{% if user.is_authenticated %}
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" id="user-dropdown" data-toggle="dropdown">
<i class="fas fa-user"></i> {{ user.username }}
</a>
<div class="dropdown-menu" aria-labelledby="user-dropdown">
<a href="{% url 'password_change' %}" class="dropdown-item">
<i class="fas fa-lock"></i> 變更密碼
</a>
<a href="{% url 'logout' %}" class="dropdown-item">
<i class="fas fa-sign-out-alt"></i> 登出
</a>
</div>
</li>
{% else %}
<li class="nav-item">
<a href="{% url 'login' %}" class="nav-link">
<i class="fas fa-sign-in-alt"></i> 登入
</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
首頁範本
建立首頁範本 templates/home.html
:
{% extends "base.html" %}
{% block content %}
<p>歡迎光臨線上教室!</p>
{% endblock %}
先放個簡單的訊息就好。
登入頁面範本
建立 templates/registration
資料夾,接著新增登入範本檔 templates/registration/login.html
:
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<div class="alert alert-danger">
{{ form.errors }}
</div>
{% endif %}
<form action="" method="POST">
{% csrf_token %}
<div id="login-form" class="form-inline">
<input type="text" name="username" placeholder="請輸入您的帳號" autofocus>
<input type="password" name="password" placeholder="請輸入您的密碼">
<input type="submit" class="btn btn-primary" value="登入">
</div>
</form>
{% endblock %}
{% block custom_scripts %}
<script>
add_css_class('#login-form input[placeholder]', 'form-control', 'mr-2');
</script>
{% endblock %}
add_css_class()
來新增頁面上的 HTML 元素所套用的 CSS 類別,藉以調整其外觀。
登出頁面範本
建立登出範本 templates/registration/logged_out.html
:
{% extends "base.html" %}
{% block content %}
<p class="alert alert-info">您已登出!!</p>
<a href="{% url 'login' %}" class="btn btn-primary">請按此處重新登入</a>
{% endblock %}
變更個人密碼頁面範本
建立變更密碼範本 templates/registration/password_change_form.html
:
{% extends "base.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<div class="card">
<div class="card-body">
<table class="table table">
{{ form.as_table }}
</table>
</div>
<div class="card-footer">
<input type="submit" value="送出" class="btn btn-primary">
</div>
</div>
</form>
{% endblock %}
{% block custom_scripts %}
<script>
add_css_class('form table input', 'form-control');
add_css_class('.errorlist', 'alert', 'alert-danger');
add_css_class('.helptext ul', 'alert', 'alert-light');
</script>
{% endblock %}
範本裡的表單欄位自行撰寫好,還是自動展開得好?
以 {{ form.as_table }}
、{{ form.as_p }}
或 {{ form.as_ul }}
在範本檔中自動展開表單欄位的話,在表單發生驗證失敗的狀況時,會自動發生錯誤的欄位加上錯誤訊息。如果沒有特殊需要的話,建議盡量以自動展開表單的方式為優先考量,避免自行手刻表單欄位的 HTML 原始碼,這樣會比較省事。
個人密碼變更完成頁面範本
變更密碼還需要變更完成的頁面範本,請建立範本檔 templates/registration/password_change_done.html
:
{
{
<p class="alert alert-info">密碼已成功變更!</p>
{
使用者帳號管理
接下來要實作帳號列表、註冊新帳號、修改使用者帳號資料、修改使用者密碼,以及切換使用者的教師角色等帳號管理相關功能。
新增路徑規則
修改應用程式 user
的路徑規則檔 user/urls.py
,
urlpatterns = [
path('', include('django.contrib.auth.urls')),
path('', UserDashboard.as_view(), name='user_dashboard'),
path('list/', UserList.as_view(), name='user_list'),
path('register/', UserRegister.as_view(), name='user_register'),
path('<int:pk>/', UserView.as_view(), name='user_view'),
path('<int:pk>/edit/', UserEdit.as_view(), name='user_edit'),
path('<int:pk>/password/', UserPasswordUpdate.as_view(), name='user_password'),
path('<int:uid>/teacher/', UserTeacherToggle.as_view(), name='user_teacher_toggle'),
]
新增處理視圖
開啟 user/views.py
,來實作各功能的處理視圖:
管理員才允許操作的混成類別
接下來實作的大部份都是管理員才允許操作的功能,為了方便程式撰寫,先定義一個協助檢查是否為管理員的混成類別:
from django.views.generic import *
from django.urls import reverse_lazy
from django.contrib.auth.mixins import *
from django.contrib.auth.models import User, Group
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q, Subquery
from django import forms
from .models import *
class SuperuserRequiredMixin(AccessMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_superuser:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
使用者註冊
class UserRegister(CreateView):
extra_context = {'title': '使用者註冊'}
model = User
fields = ['username', 'first_name', 'last_name', 'email', 'password']
template_name = 'form.html'
success_url = reverse_lazy('home')
def get_form(self):
form = super().get_form()
form.fields['first_name'].label = '真實姓名'
form.fields['first_name'].required = True
form.fields['last_name'].label = '學校名稱'
form.fields['last_name'].required = True
form.fields['password2'] = forms.CharField(label='密碼驗證', max_length=255)
return form
def form_valid(self, form):
user = form.save(commit=False)
pw1 = form.cleaned_data['password']
pw2 = form.cleaned_data['password2']
if pw1 != pw2:
form.add_error('password2', '密碼與驗證密碼不相符')
return super().form_invalid(form)
user.set_password(pw1)
return super().form_valid(form)
使用者註冊這個功能所做的,實際上就是在 User
這個資料模型新增一筆紀錄,所以可以處理視圖可以繼承 CreateView
類別來實作:
使用者列表
class UserList(SuperuserRequiredMixin, ListView):
extra_context = {'title': '使用者列表'}
model = User
ordering = ['username']
paginate_by = 20
template_name = 'user/user_list.html'
def get_queryset(self):
return super().get_queryset().prefetch_related('groups')
檢視使用者資料
class UserView(SuperuserRequiredMixin, DetailView):
model = User
template_name = 'user/user_detail.html'
context_object_name = 'tuser'
帳號管理
這個專案是要讓教師與學生在線上進行互動,大部份的操作都需要取得使用者的身分,再決定要如何處理。所以這邊先處理使用者帳號。
建立專案與應用程式
開啟終端機或命令提示字元程式,以下面指令建立一個名為 eclassroom
的新專案:
django-admin.py startproject eclassroom
指令執行完成後,會在當前的工作資料夾產生一個名為 eclassroom
的資料夾,接下來將工作目錄切換至方才建立的 eclassroom
專案資料夾,然後在專案下新增應用程式 user
:
cd eclassroom
python manage.py startapp user
接著修改專案設定檔,將它加入專案的應用程式中。請修改 eclassroom/settings.py
,修改第 28, 58, 107, 109 行,新增第 40, 122 - 125 行:
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user',
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['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',
],
},
},
]
LANGUAGE_CODE = 'zh-hant'
TIME_ZONE = 'Asia/Taipei'
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
LOGIN_URL = '/user/login/'
LOGIN_REDIRECT_URL = '/'
在專案中會使用到自訂 CSS 與 Javascript 檔案,所以要透過第 122 行指定靜態檔案的儲存位置。
使用者登入與登出
執行以下指令建立資料庫,並建立管理員帳號:
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
接著開啟網站服務:
python manage.py runserver 0.0.0.0:80
建立路徑規則
開啟專案路徑規則檔 eclassroom/urls.py
,修改第 17 行,並新增第 18, 22 - 29 行:
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('',
TemplateView.as_view(
template_name='home.html',
extra_context={'title': '歡迎光臨'}
),
name='home'
),
path('user/', include('user.urls')),
]
我們將使用者管理相關的功能都集中到 user
這個自訂的應用程式來實作,先新增該應用程式的路徑規則檔 user/urls.py
,內容如下:
from django.urls import path
from .views import *
urlpatterns = [
path('', include('django.contrib.auth.urls')),
]
登入、登出,以及個人密碼變更等功能不需要在 views.py
裡自行實作,因為 Django 內建的應用程式 django.contrib.auth
已經有現成的功能可以直接使用,所以這邊直接將 django.contrib.auth.urls
定義的路徑規則引用進來即可。
頁面範本
請先在專案資料夾內建立 templates
資料夾,用來集中存放專案會使用到的頁面範本,專案會使用到自訂的 CSS 與 Javascript 檔案等靜態資源,也請在專案資料夾內建立 static
資料夾來存放靜態檔案,另外在 static
資料夾內,分別建立 css
與 js
資料夾以便將 CSS 樣式表與 Javascript 程式檔分開放置。
至目前為止,專案資料夾的結構大致如下:
.
├── eclassroom/
├── static/
│ ├── css/
│ └── js/
├── templates/
└── user/
網站基底範本
如果覺得 Bootstrap 的原始配色看得有點膩了,又不曉得怎麼自己調整配色的話,可以上 Bootswatch(https://bootswatch.com/) 網站挑一個別人配好的順眼樣式來替換 Bootstrap 原始的設定:
這裡以 Minty 為例,點按該卡片下方的「DOWNLOAD」按鈕後會下載一個 bootstrap.min.css
的檔案,將其複製到專案的 static/css/
資料夾內。
新增網站基底範本 templates/base.html
,內容如下:
{% load static %}<!DOCTYPE html>
<html lang="zh-hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css">
<link rel="stylesheet" href="{% static 'css/custom.css' %}">
<title>線上教室</title>
</head>
<body>
{% include "navbar.html" %}
<div class="container my-3">
{% if messages %}
<section id="messages">
<ul class="messages pl-0">
{% for msg in messages %}
<li class="alert alert-{{ msg.tags }}">{{ msg }}</li>
{% endfor %}
</ul>
</section>
{% endif %}
<section id="main-content">
{% if title %}
<h1 class="title">{{ title }}</h1>
{% endif %}
{% block content %}{% endblock %}
</section>
</div>
<footer class="border-top p-1">
<div class="container">
<small class="text-muted">Copyright 2020 線上教室</small>
</div>
</footer>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="{% static 'js/custom.js' %}"></script>
{% block custom_scripts %}{% endblock %}
</body>
</html>
新增自訂的 Javascript 程式檔 static/js/custom.js
,在裡面撰寫自訂的工具函式:
function add_css_class(filter, ...classes) {
document.querySelectorAll(filter).forEach(function(item) {
item.classList.add(...classes);
});
}
add_css_class('.alert-error', 'alert-danger');
新增自訂的 CSS 樣式檔 static/css/custom.css
,填入後面會用到的自訂 CSS 規則組:
#work-detail input[name="score"] {
width: 100px;
}
#work-list .work {
padding: .75rem 1.25rem;
margin: .25rem 0;
border: 1px solid #eee;
border-left: .25rem solid #eee;
border-radius: .25rem;
}
#work-list .work-memo {
margin-top: .5rem;
}
#work-list .work-meta {
display: flex;
justify-content: space-between;
}
#work-list .work-score {
font-size: 200%;
color: #FF7851;
}
.bd-callout-warning {
border-left-color: #f0ad4e;
}
.bd-callout-info {
border-left-color: #5bc0de;
}
.bd-callout-danger {
border-left-color: #FF7851;
}
#work-detail .score {
margin: 5pt;
font-size: 20pt;
color: red;
border-bottom: 3px double red;
text-align: right;
}
#work-list .score {
color: red;
border-bottom: 3px double red;
}
接著新增導覽列範本 templates/navbar.html
:
<nav class="navbar navbar-expand-md navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/">線上教室</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarMenu" aria-controls="navbarMenu" aria-expanded="false" aria-label="Toggle navigation">
<i class="navbar-toggler-icon"></i>
</button>
<div class="collapse navbar-collapse" id="navbarMenu">
<ul class="navbar-nav">
{% if user.is_authenticated %}
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" id="user-dropdown" data-toggle="dropdown">
<i class="fas fa-user"></i> {{ user.username }}
</a>
<div class="dropdown-menu" aria-labelledby="user-dropdown">
<a href="{% url 'password_change' %}" class="dropdown-item">
<i class="fas fa-lock"></i> 變更密碼
</a>
<a href="{% url 'logout' %}" class="dropdown-item">
<i class="fas fa-sign-out-alt"></i> 登出
</a>
</div>
</li>
{% else %}
<li class="nav-item">
<a href="{% url 'login' %}" class="nav-link">
<i class="fas fa-sign-in-alt"></i> 登入
</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
首頁範本
建立首頁範本 templates/home.html
:
{% extends "base.html" %}
{% block content %}
<p>歡迎光臨線上教室!</p>
{% endblock %}
先放個簡單的訊息就好。
登入頁面範本
建立 templates/registration
資料夾,接著新增登入範本檔 templates/registration/login.html
:
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<div class="alert alert-danger">
{{ form.errors }}
</div>
{% endif %}
<form action="" method="POST">
{% csrf_token %}
<div id="login-form" class="form-inline">
<input type="text" name="username" placeholder="請輸入您的帳號" autofocus>
<input type="password" name="password" placeholder="請輸入您的密碼">
<input type="submit" class="btn btn-primary" value="登入">
</div>
</form>
{% endblock %}
{% block custom_scripts %}
<script>
add_css_class('#login-form input[placeholder]', 'form-control', 'mr-2');
</script>
{% endblock %}
add_css_class()
來新增頁面上的 HTML 元素所套用的 CSS 類別,藉以調整其外觀。
登出頁面範本
建立登出範本 templates/registration/logged_out.html
:
{% extends "base.html" %}
{% block content %}
<p class="alert alert-info">您已登出!!</p>
<a href="{% url 'login' %}" class="btn btn-primary">請按此處重新登入</a>
{% endblock %}
變更個人密碼頁面範本
建立變更密碼範本 templates/registration/password_change_form.html
:
{% extends "base.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<div class="card">
<div class="card-body">
<table class="table table">
{{ form.as_table }}
</table>
</div>
<div class="card-footer">
<input type="submit" value="送出" class="btn btn-primary">
</div>
</div>
</form>
{% endblock %}
{% block custom_scripts %}
<script>
add_css_class('form table input', 'form-control');
add_css_class('.errorlist', 'alert', 'alert-danger');
add_css_class('.helptext ul', 'alert', 'alert-light');
</script>
{% endblock %}
範本裡的表單欄位自行撰寫好,還是自動展開得好?
以 {{ form.as_table }}
、{{ form.as_p }}
或 {{ form.as_ul }}
在範本檔中自動展開表單欄位的話,在表單發生驗證失敗的狀況時,會自動發生錯誤的欄位加上錯誤訊息。如果沒有特殊需要的話,建議盡量以自動展開表單的方式為優先考量,避免自行手刻表單欄位的 HTML 原始碼,這樣會比較省事。
個人密碼變更完成頁面範本
變更密碼還需要變更完成的頁面範本,請建立範本檔 templates/registration/password_change_done.html
:
{
{
<p class="alert alert-info">密碼已成功變更!</p>
{
使用者帳號管理
接下來要實作帳號列表、註冊新帳號、修改使用者帳號資料、修改使用者密碼,以及切換使用者的教師角色等帳號管理相關功能。
新增路徑規則
修改應用程式 user
的路徑規則檔 user/urls.py
,
urlpatterns = [
path('', include('django.contrib.auth.urls')),
path('', UserDashboard.as_view(), name='user_dashboard'),
path('list/', UserList.as_view(), name='user_list'),
path('register/', UserRegister.as_view(), name='user_register'),
path('<int:pk>/', UserView.as_view(), name='user_view'),
path('<int:pk>/edit/', UserEdit.as_view(), name='user_edit'),
path('<int:pk>/password/', UserPasswordUpdate.as_view(), name='user_password'),
path('<int:uid>/teacher/', UserTeacherToggle.as_view(), name='user_teacher_toggle'),
]
新增處理視圖
開啟 user/views.py
,來實作各功能的處理視圖:
管理員才允許操作的混成類別
接下來實作的大部份都是管理員才允許操作的功能,為了方便程式撰寫,先定義一個協助檢查是否為管理員的混成類別:
from django.views.generic import *
from django.urls import reverse_lazy
from django.contrib.auth.mixins import *
from django.contrib.auth.models import User, Group
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q, Subquery
from django import forms
from .models import *
class SuperuserRequiredMixin(AccessMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_superuser:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
- ++第 13 行++,`request.user` 代表著目前正進行操作的使用者,它的 `is_superuser` 屬性的意思是該使用者是否為管理員。
使用者註冊
class UserRegister(CreateView):
extra_context = {'title': '使用者註冊'}
model = User
fields = ['username', 'first_name', 'last_name', 'email', 'password']
template_name = 'form.html'
success_url = reverse_lazy('home')
def get_form(self):
form = super().get_form()
form.fields['first_name'].label = '真實姓名'
form.fields['first_name'].required = True
form.fields['last_name'].label = '學校名稱'
form.fields['last_name'].required = True
form.fields['password2'] = forms.CharField(label='密碼驗證', max_length=255)
return form
def form_valid(self, form):
user = form.save(commit=False)
pw1 = form.cleaned_data['password']
pw2 = form.cleaned_data['password2']
if pw1 != pw2:
form.add_error('password2', '密碼與驗證密碼不相符')
return super().form_invalid(form)
user.set_password(pw1)
return super().form_valid(form)
使用者註冊這個功能所做的,實際上就是在 User
這個資料模型新增一筆紀錄,所以可以處理視圖可以繼承 CreateView
類別來實作:
使用者列表
class UserList(SuperuserRequiredMixin, ListView):
extra_context = {'title': '使用者列表'}
model = User
ordering = ['username']
paginate_by = 20
template_name = 'user/user_list.html'
def get_queryset(self):
return super().get_queryset().prefetch_related('groups')
檢視使用者資料
class UserView(SuperuserRequiredMixin, DetailView):
model = User
template_name = 'user/user_detail.html'
context_object_name = 'tuser'
準備工作都完成了,然後就可以撰寫頁面範本了。請先建立 templates/user
資料夾,然後新增帳號列表範本 templates/user/user_list.html
:
{% extends "base.html" %}
{% load user_tag %}
{% block content %}
<div class="card">
<div id="user-list" class="card-body">
<table class="table table-sm">
<thead>
<tr>
<th>帳號</th>
<th>姓名</th>
<th>學校</th>
<th>教師</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for user in user_list %}
<tr>
<td>
<a href="{% url 'user_view' user.id %}">{{ user.username }}</a>
</td>
<td>{{ user.first_name }}</td>
<td>{{ user.last_name }}</td>
<td>{% if user|is_teacher %}<i class="fas fa-check"></i>{% endif %}</td>
<td>
<a href="{% url 'user_edit' user.id %}" class="btn btn-sm btn-primary">修改</a>
<a href="{% url 'user_password' user.id %}" class="btn btn-sm btn-secondary">密碼</a>
<a href="{% url 'user_teacher_toggle' user.id %}?next={{ request.path|urlencode }}" class="btn btn-sm btn-light">切換教師</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="card-footer">
{% include "pagination.html" %}
</div>
</div>
{% endblock %}
列表的頁面常會用到分頁的功能,所以還得新增分頁控制項的範本檔 templates/pagination.html
,內容可以直接複製前面幾個案例的內容:
{% if is_paginated %}
{% with btn_class="btn btn-sm btn-primary" %}
<div class="mt-1">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}" class="{{ btn_class }}">
<i class="fas fa-chevron-circle-left"></i>上一頁
</a>
{% endif %}
{% for page in paginator.page_range %}
{% if page == page_obj.number %}
<button class="{{ btn_class }}" disabled>{{ page }}</button>
{% else %}
<a href="?page={{ page }}" class="{{ btn_class }}">{{ page }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}" class="{{ btn_class }}">
下一頁<i class="fas fa-chevron-circle-right"></i>
</a>
{% endif %}
</div>
{% endwith %}
{% endif %}
查看使用者
新增查看使用者頁面範本 templates/user/user_detail.html
,內容如下:
{% extends "base.html" %}
{% block content %}
<div class="card">
<div class="card-header">
<h5 class="card-title">{{ tuser.username }}</h5>
<h6 class="card-subtitle text-muted">{{ tuser.first_name }}</h6>
</div>
<div class="card-body">
</div>
</div>
{% endblock %}
共用的表單範本
新增、修改資料時,如果沒有特別需要的話,通常表單頁面範本的內容都差不多,所以可以共用同一個表單範本。
新增 templates/form.html
:
{% extends "base.html" %}
{% block content %}
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="card">
<div class="card-body">
<table class="table table">
{{ form.as_table }}
</table>
</div>
<div class="card-footer">
<input type="submit" value="送出" class="btn btn-primary">
</div>
</div>
</form>
{% endblock %}
{% block custom_scripts %}
<script>
add_css_class('form table input', 'form-control');
add_css_class('.errorlist', 'alert', 'alert-danger');
add_css_class('.helptext ul', 'alert', 'alert-light');
</script>
{% endblock %}
修改登入頁面範本
修改登入頁面範本 templates/registration/login.html
,新增第 15, 23 行以加入註冊按鈕:
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<div class="alert alert-danger">
{{ form.errors }}
</div>
{% endif %}
<form action="" method="POST">
{% csrf_token %}
<div id="login-form" class="form-inline">
<input type="text" name="username" placeholder="請輸入您的帳號" autofocus>
<input type="password" name="password" placeholder="請輸入您的密碼">
<input type="submit" class="btn btn-primary" value="登入">
<a href="{% url 'user_register' %}" class="btn btn-light">註冊</a>
</div>
</form>
{% endblock %}
{% block custom_scripts %}
<script>
add_css_class('#login-form input[placeholder]', 'form-control', 'mr-2');
add_css_class('#login-form .btn', 'mr-2');
</script>
{% endblock %}
帳號管理
這個專案是要讓教師與學生在線上進行互動,大部份的操作都需要取得使用者的身分,再決定要如何處理。所以這邊先處理使用者帳號。
建立專案與應用程式
開啟終端機或命令提示字元程式,以下面指令建立一個名為
eclassroom
的新專案:指令執行完成後,會在當前的工作資料夾產生一個名為
eclassroom
的資料夾,接下來將工作目錄切換至方才建立的eclassroom
專案資料夾,然後在專案下新增應用程式user
:接著修改專案設定檔,將它加入專案的應用程式中。請修改
eclassroom/settings.py
,修改第 28, 58, 107, 109 行,新增第 40, 122 - 125 行:在專案中會使用到自訂 CSS 與 Javascript 檔案,所以要透過第 122 行指定靜態檔案的儲存位置。
使用者登入與登出
執行以下指令建立資料庫,並建立管理員帳號:
接著開啟網站服務:
建立路徑規則
開啟專案路徑規則檔
eclassroom/urls.py
,修改第 17 行,並新增第 18, 22 - 29 行:我們將使用者管理相關的功能都集中到
user
這個自訂的應用程式來實作,先新增該應用程式的路徑規則檔user/urls.py
,內容如下:登入、登出,以及個人密碼變更等功能不需要在
views.py
裡自行實作,因為 Django 內建的應用程式django.contrib.auth
已經有現成的功能可以直接使用,所以這邊直接將django.contrib.auth.urls
定義的路徑規則引用進來即可。頁面範本
請先在專案資料夾內建立
templates
資料夾,用來集中存放專案會使用到的頁面範本,專案會使用到自訂的 CSS 與 Javascript 檔案等靜態資源,也請在專案資料夾內建立static
資料夾來存放靜態檔案,另外在static
資料夾內,分別建立css
與js
資料夾以便將 CSS 樣式表與 Javascript 程式檔分開放置。至目前為止,專案資料夾的結構大致如下:
網站基底範本
如果覺得 Bootstrap 的原始配色看得有點膩了,又不曉得怎麼自己調整配色的話,可以上 Bootswatch(https://bootswatch.com/) 網站挑一個別人配好的順眼樣式來替換 Bootstrap 原始的設定:
這裡以 Minty 為例,點按該卡片下方的「DOWNLOAD」按鈕後會下載一個
bootstrap.min.css
的檔案,將其複製到專案的static/css/
資料夾內。新增網站基底範本
templates/base.html
,內容如下:新增自訂的 Javascript 程式檔
static/js/custom.js
,在裡面撰寫自訂的工具函式:新增自訂的 CSS 樣式檔
static/css/custom.css
,填入後面會用到的自訂 CSS 規則組:接著新增導覽列範本
templates/navbar.html
:首頁範本
建立首頁範本
templates/home.html
:先放個簡單的訊息就好。
登入頁面範本
建立
templates/registration
資料夾,接著新增登入範本檔templates/registration/login.html
:add_css_class()
來新增頁面上的 HTML 元素所套用的 CSS 類別,藉以調整其外觀。登出頁面範本
建立登出範本
templates/registration/logged_out.html
:變更個人密碼頁面範本
建立變更密碼範本
templates/registration/password_change_form.html
:範本裡的表單欄位自行撰寫好,還是自動展開得好?
以
{{ form.as_table }}
、{{ form.as_p }}
或{{ form.as_ul }}
在範本檔中自動展開表單欄位的話,在表單發生驗證失敗的狀況時,會自動發生錯誤的欄位加上錯誤訊息。如果沒有特殊需要的話,建議盡量以自動展開表單的方式為優先考量,避免自行手刻表單欄位的 HTML 原始碼,這樣會比較省事。個人密碼變更完成頁面範本
變更密碼還需要變更完成的頁面範本,請建立範本檔
templates/registration/password_change_done.html
:使用者帳號管理
接下來要實作帳號列表、註冊新帳號、修改使用者帳號資料、修改使用者密碼,以及切換使用者的教師角色等帳號管理相關功能。
新增路徑規則
修改應用程式
user
的路徑規則檔user/urls.py
,新增處理視圖
開啟
user/views.py
,來實作各功能的處理視圖:管理員才允許操作的混成類別
接下來實作的大部份都是管理員才允許操作的功能,為了方便程式撰寫,先定義一個協助檢查是否為管理員的混成類別:
使用者註冊
使用者註冊這個功能所做的,實際上就是在
User
這個資料模型新增一筆紀錄,所以可以處理視圖可以繼承CreateView
類別來實作:使用者列表
檢視使用者資料
帳號管理
這個專案是要讓教師與學生在線上進行互動,大部份的操作都需要取得使用者的身分,再決定要如何處理。所以這邊先處理使用者帳號。
建立專案與應用程式
開啟終端機或命令提示字元程式,以下面指令建立一個名為
eclassroom
的新專案:指令執行完成後,會在當前的工作資料夾產生一個名為
eclassroom
的資料夾,接下來將工作目錄切換至方才建立的eclassroom
專案資料夾,然後在專案下新增應用程式user
:接著修改專案設定檔,將它加入專案的應用程式中。請修改
eclassroom/settings.py
,修改第 28, 58, 107, 109 行,新增第 40, 122 - 125 行:在專案中會使用到自訂 CSS 與 Javascript 檔案,所以要透過第 122 行指定靜態檔案的儲存位置。
使用者登入與登出
執行以下指令建立資料庫,並建立管理員帳號:
接著開啟網站服務:
建立路徑規則
開啟專案路徑規則檔
eclassroom/urls.py
,修改第 17 行,並新增第 18, 22 - 29 行:我們將使用者管理相關的功能都集中到
user
這個自訂的應用程式來實作,先新增該應用程式的路徑規則檔user/urls.py
,內容如下:登入、登出,以及個人密碼變更等功能不需要在
views.py
裡自行實作,因為 Django 內建的應用程式django.contrib.auth
已經有現成的功能可以直接使用,所以這邊直接將django.contrib.auth.urls
定義的路徑規則引用進來即可。頁面範本
請先在專案資料夾內建立
templates
資料夾,用來集中存放專案會使用到的頁面範本,專案會使用到自訂的 CSS 與 Javascript 檔案等靜態資源,也請在專案資料夾內建立static
資料夾來存放靜態檔案,另外在static
資料夾內,分別建立css
與js
資料夾以便將 CSS 樣式表與 Javascript 程式檔分開放置。至目前為止,專案資料夾的結構大致如下:
網站基底範本
如果覺得 Bootstrap 的原始配色看得有點膩了,又不曉得怎麼自己調整配色的話,可以上 Bootswatch(https://bootswatch.com/) 網站挑一個別人配好的順眼樣式來替換 Bootstrap 原始的設定:
這裡以 Minty 為例,點按該卡片下方的「DOWNLOAD」按鈕後會下載一個
bootstrap.min.css
的檔案,將其複製到專案的static/css/
資料夾內。新增網站基底範本
templates/base.html
,內容如下:新增自訂的 Javascript 程式檔
static/js/custom.js
,在裡面撰寫自訂的工具函式:新增自訂的 CSS 樣式檔
static/css/custom.css
,填入後面會用到的自訂 CSS 規則組:接著新增導覽列範本
templates/navbar.html
:首頁範本
建立首頁範本
templates/home.html
:先放個簡單的訊息就好。
登入頁面範本
建立
templates/registration
資料夾,接著新增登入範本檔templates/registration/login.html
:add_css_class()
來新增頁面上的 HTML 元素所套用的 CSS 類別,藉以調整其外觀。登出頁面範本
建立登出範本
templates/registration/logged_out.html
:變更個人密碼頁面範本
建立變更密碼範本
templates/registration/password_change_form.html
:範本裡的表單欄位自行撰寫好,還是自動展開得好?
以
{{ form.as_table }}
、{{ form.as_p }}
或{{ form.as_ul }}
在範本檔中自動展開表單欄位的話,在表單發生驗證失敗的狀況時,會自動發生錯誤的欄位加上錯誤訊息。如果沒有特殊需要的話,建議盡量以自動展開表單的方式為優先考量,避免自行手刻表單欄位的 HTML 原始碼,這樣會比較省事。個人密碼變更完成頁面範本
變更密碼還需要變更完成的頁面範本,請建立範本檔
templates/registration/password_change_done.html
:使用者帳號管理
接下來要實作帳號列表、註冊新帳號、修改使用者帳號資料、修改使用者密碼,以及切換使用者的教師角色等帳號管理相關功能。
新增路徑規則
修改應用程式
user
的路徑規則檔user/urls.py
,新增處理視圖
開啟
user/views.py
,來實作各功能的處理視圖:管理員才允許操作的混成類別
接下來實作的大部份都是管理員才允許操作的功能,為了方便程式撰寫,先定義一個協助檢查是否為管理員的混成類別:
使用者註冊
使用者註冊這個功能所做的,實際上就是在
User
這個資料模型新增一筆紀錄,所以可以處理視圖可以繼承CreateView
類別來實作:使用者列表
檢視使用者資料
準備工作都完成了,然後就可以撰寫頁面範本了。請先建立
templates/user
資料夾,然後新增帳號列表範本templates/user/user_list.html
:列表的頁面常會用到分頁的功能,所以還得新增分頁控制項的範本檔
templates/pagination.html
,內容可以直接複製前面幾個案例的內容:查看使用者
新增查看使用者頁面範本
templates/user/user_detail.html
,內容如下:共用的表單範本
新增、修改資料時,如果沒有特別需要的話,通常表單頁面範本的內容都差不多,所以可以共用同一個表單範本。
新增
templates/form.html
:修改登入頁面範本
修改登入頁面範本
templates/registration/login.html
,新增第 15, 23 行以加入註冊按鈕: