實戰:留言板

接下來這個範例不大,但是可以讓你再重頭複習一遍用 Django 建立網站專案的過程。這個簡單的留言板網站,允許任何人留言,但僅允許網站管理員登入後刪除留言。所以這個範例中也會示範如何讓登入及登出使用者。

建立專案與應用程式

  1. 在終端機中下達指令建立一個新的專案

    django-admin.py startproject guestbook

    打完指令後,會產生一個guestbook的資料夾

    guestbook/
        manage.py
        guestbook/
            __init__.py
            settings.py
            urls.py
            wsgi.py
    
  2. 建立一個應用程式

    cd guestbook python manage.py startapp web
  3. 開啟專案設定檔 guestbook/guestbook/settings.py,修改第 28 行,並新增第 34 行

    ALLOWED_HOSTS = ['*'] # Application definition INSTALLED_APPS = [ 'web', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]

    另外,修改同一檔案的 第 107, 109 行

    LANGUAGE_CODE = 'zh-hant' TIME_ZONE = 'Asia/Taipei'

定義資料模型

修改 guestbook/web/models.py,新增第 5 - 12 行來定義留言資料模型:

from django.db import models # Create your models here. class Message(models.Model): user = models.CharField("姓名", max_length=50) subject = models.CharField("主旨", max_length=200) content = models.TextField("內容") publication_date = models.DateTimeField("留言日期", auto_now_add=True) def __str__(self): return self.subject

執行以下指令建立資料庫與網站管理帳號:

python manage.py makemigrations python manage.py migrate python manage.py createsuperuser

完成後,啟用專案網站服務

python manage.py runserver 0.0.0.0:80

視圖、網址、範本、表單

我們要建立相對應的網頁,首先定義專案的路徑規則。開啟 guestbook/guestbook/urls.py,修改為以下程式碼:

from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('web.urls')), ]

接著定義自訂應用程式 web 的路徑規則。新增檔案 guestbook/web/urls.py

from django.urls import path from django.views.generic import RedirectView from .views import * urlpatterns = [ path('', RedirectView.as_view(url='message/')), path('message/', MessageList.as_view()), path('message/<int:pk>/', MessageDetail.as_view()), path('message/create/', MessageCreate.as_view()), ]

開啟 guestbook/web/views.py,修改為以下程式碼。

from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView from django.urls import reverse from .models import Message # 留言列表 class MessageList(ListView): model = Message ordering = ['-id'] # 以 id 欄位值由大至小反向排序 # 留言檢視 class MessageDetail(DetailView): model = Message # 新增留言 class MessageCreate(CreateView): model = Message fields = ['user', 'subject', 'content'] # 僅顯示 user, subject, content 這 3 個欄位 success_url = '/message/' # 新增成功後,導向留言列表 template_name = 'form.html' # 指定欲使用的頁面範本

建立目錄 guestbook/web/templates/web

新增專案網站基底範本 guestbook/web/templates/base.html

<!DOCTYPE html> <html lang="zh-hant"> <head> <title>留言板</title> </head> <body> <h1>留言板</h1> <div> <a href="/">首頁</a> <a href="/message/create/">新增留言</a> </div> <div> {% block content %}{% endblock %} </div> </body> </html>

新增留言列表頁面範本 guestbook/web/templates/web/message_list.html

{% extends 'base.html' %} {% block content %} <ul> {% for message in message_list %} <li> {{ message.publication_date }} <a href="{{ message.id }}">{{ message.subject }}</a> </li> {% endfor %} </ul> {% endblock %}

新增留言檢視頁面範本 guestbook/web/templates/web/message_detail.html

{% extends "base.html" %} {% block content %} <h2>姓名</h2> {{ message.user }}<BR> <h2>主旨</h2> {{ message.subject }}<BR> <h2>內容</h2> {{ message.content|linebreaks }}<BR> {% endblock %}

新增共用表單頁面範本 guestbook/web/templates/form.html

{% extends 'base.html' %} {% block content %} <form action="" method="post"> {% csrf_token %} <table> {{ form.as_table }} </table> <input type="submit" value="送出" /> </form> {% endblock %}

使用者登入與登出

修改專案設定檔 guestbook/guestbook/settings.py,新增以下 2 行

# 登入後重新導向首頁 (預設會導向 /accounts/profile/) LOGIN_REDIRECT_URL = '/'

修改 guestbook/guestbook/urls.py

urlpatterns = [ path('admin/', admin.site.urls), path('', include('web.urls')), path('accounts/', include('django.contrib.auth.urls')), ]

開啟 guestbook/guestbook/settings.py,修改程式碼: 第4行

TEMPLATES = [ { ... 'DIRS': ['templates'], 'APP_DIRS': True, ...

新增資料夾 guestbook/templates/registration

新增登入頁面範本 guestbook/templates/registration/login.html

{% extends "base.html" %} {% block content %} <form action="" method="post"> {% csrf_token %} {{ form.as_p }} <div> <input type="submit" value="登入"> </div> </form> {% endblock %}

新增登出頁面範本 guestbook/templates/registration/logged_out.html

{% extends "base.html" %} {% block content %} <p>您已登出!!</p> <a href="{% url 'login'%}">請按此處重新登入</a> {% endblock %}

修改專案基底頁面範本 guestbook/web/templates/base.htm,修改為以下程式碼:

<!DOCTYPE html> <html lang="zh-hant"> <head> <title>留言板</title> </head> <body> <h1>留言板</h1> <div> <a href="/">首頁</a> <a href="/message/create">新增留言</a> {% if user.is_authenticated %} {{ user.username }} <a href="{% url 'logout' %}">登出</a> {% else %} <a href="{% url 'login' %}">登入</a> {% endif %} </div> <hr> <div> {% block content %}{% endblock %} </div> </body> </html>

限制登入後才能刪除留言

開啟 guestbook/web/urls.py,新增第 10 行,定義「刪除留言」的存取路徑:

from django.urls import path from django.views.generic import RedirectView from .views import * urlpatterns = [ path('', RedirectView.as_view(url='message/')), path('message/', MessageList.as_view()), path('message/<int:pk>/', MessageDetail.as_view()), path('message/create/', MessageCreate.as_view()), path('message/<int:pk>/delete/', MessageDelete.as_view()), ]

開啟 guestbook/web/views.py,新增以下程式碼:

from django.contrib.auth.mixins import LoginRequiredMixin # 刪除留言 class MessageDelete(LoginRequiredMixin, DeleteView): model = Message success_url = '/message/' # 刪除成功返回留言列表 template_name = 'confirm_delete.html'

新增刪除留言頁面範本 guestbook/web/templates/confirm_delete.html

{% extends "base.html" %} {% block content %} <h2>刪除記錄</h2> <p>您確定要刪除「{{ object }}」這筆記錄嗎?</p> <form action="" method="POST"> {% csrf_token %} <input type="submit" action="" value="是的,我要刪除" /> </form> {% endblock %}

開啟 guestbook/web/templates/web/message_list.html,新增第 8 - 10 行,若使用者已登入,則顯示刪除留言的連結:

{% extends "base.html" %} {% block content %} <ul> {% for message in message_list %} <li> {{ message.publication_date }} {% if user.is_authenticated %} <a href="{{ message.id }}/delete/">刪除</a> {% endif %} <a href="{{ message.id }}/">{{ message.subject }}</a> </li> {% endfor %} </ul> {% endblock %}