iT邦幫忙

2022 iThome 鐵人賽

DAY 28
0
自我挑戰組

從0 到 50 初探 如何使用Django 架構出一個網站系列 第 28

Day-28 - Class Based View - 幫助你快速建置常用頁面的好方法!

  • 分享至 

  • xImage
  •  

前言

前面講了很久MTV不同的功能,其實用到的都還是我們一開始在View裡面提到的Function Based View。
為什麼會等到現在才講 Class Based View呢?
因為裡面會牽扯到Template和Model 所以我想要在講完Template和Model之後
再帶到Class Based View

Class Based View

在講之前我們要先來回顧一下什麼是CBVs(Class Based Views),以及為什麼要去使用他
我們原本使用的FBVs(Function Based Views)就已經很直觀很好用了怎麼還要使用CBV呢?
相較於FBVs:

  • 可以使用繼承的方式減少程式碼的重複性,並加快開發速度
  • 再使用HTTP的GET或POST等方法時,不用使用if條件判斷的方式,而是可以使用不同的method來達成。

FBVs 用法

from django.http import HttpResponse

def my_view(request):
    if request.method == 'GET':
        # <view logic>
        return HttpResponse('result')

CBVs 用法

from django.http import HttpResponse
from django.views import View

class MyView(View):
    def get(self, request):
        # <view logic>
        return HttpResponse('result')

不僅如此Django還內建許多好用的基本類別:

  • TemplateView
  • FormView
  • CreateView
  • ListView
  • DetailView
  • UpdateView
  • DeleteView

TemplateView 類別

以最基本的 TemplateView 為例,它的用途就是將自定義的模板(Template)呈現出來,並且可以包含Model的查找
原本 render html 的方式,我們可以繼承 TemplateView 的類別
並利用覆寫(overriding)來將它改成我們要使用的模板。

views.py

from django.shortcuts import render
from django.http import HttpResponse
from django.views.generic import TemplateView

#FBVs的方法
def home(request):
    return render(request, "home.html")

#CBVs的方法
class home_view(TemplateView):
    template_name = 'home.html'

我們要先把 class import進來
在urls裡面,因為home不再是函式,所以在建立path時,需要讓class去使用as_view的方法
as_view會調用dispatch的方法,dispatch會去查找是否有這個views的class,如果有就會去呼叫他,並回傳對應的 view,因此在CBVs 若要呼叫view,需要使用as_view。

urls.py

from .views import home_view
from . import views
urlpatterns = [
    #FBVs的方法
    
    path('', views.home, name = "home" ),
    
    #CBVs的方法
    
    path('', home_view.as_view(), name = "home"),
]

呈現畫面

https://ithelp.ithome.com.tw/upload/images/20221012/201509276ACMTVEYGt.png

CreateView 類別

CreateView 的功能就是能夠快速的用 Model 建立 Form,不用額外再建立ModelForm。
用法很簡單,首先先建立Model

models.py

from unittest.util import _MAX_LENGTH
from django.db import models

# Create your models here.
class account_info(models.Model):
    account_name = models.CharField(max_length= 10)
    account_email = models.EmailField()
    account_order = models.DateField()
    

views.py

再來到 views.py 建立 CreateView
如果你沒有特別設置 template_name的話,他就會自動去找 model名稱_form 的html檔案
你也可以自己設定 tempalte_name 的路徑
fields 也是,用__all__ 的話就是所有欄位都會顯示
也可以用 list 來呈現自己決定要設定的欄位

from django.views.generic import TemplateView, CreateView
from .models import account_info
from django.urls import reverse, reverse_lazy

# Create your views here.
class OrderCreateView(CreateView):
    #account_form.html 預設會去找這個html
    #template_name = "" 也可以自己定義
    model = account_info 
    #fields = ["account_name"] :決定要設定的欄位
    fields = "__all__"
    success_url = reverse_lazy("Order_System:finish")

class FinishView(TemplateView):
    template_name = "Django_app/finish.html"

account_info_form.html

我沒有設置 template_name 所以創立了一個 account_info_form.html
可以直接呼叫{{form}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form method="POST">
        {% csrf_token %}
        {{form.as_p}}
        <input type = "submit" value = "預約">
    </form>
    
</body>
</html>

urls.py

接著設置好 urls 就可以了。

from django.contrib import admin
from django.urls import path, include
from .views import OrderCreateView, FinishView

app_name = 'Order_System'
urlpatterns = [
    path('', OrderCreateView.as_view() , name = 'Odrer'),
    path('finish/', FinishView.as_view() , name = 'finish'),
]

我們可以直接打開畫面並填入表單

https://ithelp.ithome.com.tw/upload/images/20221013/20150927a6eCj0EYJk.png

完成之後就會跳轉到我們設置的 finish

https://ithelp.ithome.com.tw/upload/images/20221013/20150927NZfY3D2Mp5.png

我們就可以到後台看到我們剛剛提交的表單資料

https://ithelp.ithome.com.tw/upload/images/20221013/20150927AND3sadcMW.png

ListView 清單檢視類別

ListView 是用來呈現資料列表的,我們可以用剛剛建立的資料來示範。

views.py

首先一樣在views裡建立一個 ListView
接著一樣設定我們的Model
一樣的是如果沒有特別設定template_name, 則Django會自動去找 model名稱_list
設定 queryset 可以對資料進行邏輯的操作,操作方式和我們之前在 model 裡面操作一樣。
context_object_name 可以設定我們在html接收參數時的名稱,若沒有設定則會自動設定成 object_list

class OrderListView(ListView):
    # account_info_list.html 
    # context_object_name = Order_list
    model = account_info 
    # queryset = account_info.objects.all()

urls.py

設定 urls

from django.urls import path
from .views import OrderCreateView, FinishView, OrderListView

app_name = 'Order_System'
urlpatterns = [
    path('', OrderCreateView.as_view() , name = 'Odrer'),
    path('finish/', FinishView.as_view() , name = 'finish'),
    path('orderlist/', OrderListView.as_view(), name = 'order_list'),
]

account_info_list.html

因為我們沒有設定 context_object_name 所以我們用 object_list 來進行呼叫。
並將預約人的姓名和預約的時間調出來

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<h1>預約資料表</h1>
<body>
    <form method="POST">
        {% for data in object_list %}
            <li>預約人姓名:{{data.account_name}} 預約時間 {{data.account_order}} </li>
        {% endfor %}
    </form>
    
</body>
</html>

DetailView 類別

和 ListView 類似, DetailView 可以更詳細的呈現單一筆資料
可以用:

  • pk
  • slugFiled
    來進行呼叫

models.py

首先我們將 models.py 的account_name 裡加上primary_key = True 的屬性,這裡的用途是讓填寫的account_name做為table的主鍵,修改完後記得要migrate

from django.db import models
from datetime import datetime
# Create your models here.
class account_info(models.Model):
    account_name = models.CharField(max_length= 10,primary_key=True)
    account_email = models.EmailField()
    account_order = models.DateField(default = datetime.now())

views.py

views 裡面直接把 model設定好即可
這裡一樣可以自己設定要去抓的Template以及傳到html的object名稱
沒設定的話預設會是 account_info_detail.html
資料預設則會是object

class OrderDetailView(DetailView):
    #account_info_detail.html 預設的路徑
    #context_object_name = ''
    model = account_info

account_info_detail.html

因為沒有設置 context_object_name, 所以用 object 來呼叫我們的資料

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<h1>預約資料表</h1>
<body>
    <form method="POST">       
        <li>預約人姓名:{{object.account_name}} 預約時間 {{object.account_order}} </li>
    </form>
    
</body>
</html>

urls

接著我們就可以使用Dynamic path 將 pk 傳過去

from django.urls import path
from .views import OrderCreateView, FinishView, OrderListView, OrderDetailView

app_name = 'Order_System'
urlpatterns = [
    path('', OrderCreateView.as_view() , name = 'Odrer'),
    path('finish/', FinishView.as_view() , name = 'finish'),
    path('orderlist/', OrderListView.as_view(), name = 'order_list'),
    path('detail/<str:pk>', OrderDetailView.as_view(), name = 'orderdetail')
]

呈現畫面

就可以看到我們可以快速客製化我們單一資料的詳細資料頁面,
當網址輸入是小明時能快速呈現小明的資料

http://127.0.0.1:8000/reserve/detail/小明

https://ithelp.ithome.com.tw/upload/images/20221013/20150927KKjUZH9h8P.png

UpdateView 類別

UpdateView 的用途就是用來修改已經存在的資料,和CreateView用法非常像
同時他在使用時也需要用pk 或 slug來傳送

views.py

我們一樣定義好Model 和 Fields
這裡我不想讓account_name被修改,因此我只開放了兩個欄位
除此以外若沒有在 model裡設定 get_absolute_url 的話
則需要設置一個success_url 做成功修改後的網址導向
我們一樣讓他導去 finish

這裡有遇到一個小插曲,如果今天沒有設定 template_name 的話,submit按鈕的value會和account_info_form.html 一樣而不會是我們建立的account_info_update.html裡面所設定的

class OrderUpdateView(UpdateView):
    template_name = 'Django_app/account_info_update.html'
    model = account_info
    fields = ['account_email', 'account_order']
    success_url = reverse_lazy("Order_System:finish")

account_info_update.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form method="POST">
        {% csrf_token %}
        {{form.as_p}}
        <input type = "submit" value = "儲存">
    </form>
    
</body>
</html>

urls.py

同樣地,我們將pk做為傳送點

from django.urls import path
from .views import OrderCreateView, FinishView, OrderListView, OrderDetailView, OrderUpdateView

app_name = 'Order_System'
urlpatterns = [
    path('', OrderCreateView.as_view() , name = 'Odrer'),
    path('finish/', FinishView.as_view() , name = 'finish'),
    path('orderlist/', OrderListView.as_view(), name = 'order_list'),
    path('detail/<str:pk>', OrderDetailView.as_view(), name = 'orderdetail'),
    path('Update/<str:pk>', OrderUpdateView.as_view(), name = 'OrderUpdate')
]

呈現畫面

我們可以將預約日期改成 10-14 並儲存

https://ithelp.ithome.com.tw/upload/images/20221013/20150927jCqdBJruTp.png

再回到DetailView 頁面,就會看到紀錄修改成功了!

https://ithelp.ithome.com.tw/upload/images/20221013/20150927rkgF4NNXNM.png

DeleteView 類別

DeleteView的用途是將建立好的資料刪除,當收到request method為POST時,則將該資料刪除,若收到GET則呈現此View。
DeleteView的目的是提供一個確認的頁面,方便使用者確認刪除。

views.py

views裡面一樣要設定model
還有跳轉頁面
他和DetailView很像,若沒設置Template name的話,裡面預設的Template是 [Modelname]__confirm_delete.html
context_object_name 也可以進行設定

class OrderDeleteView(DeleteView):
    model = account_info
    success_url = reverse_lazy("Order_System:Odrer")

urls.py

from django.urls import path
from .views import OrderCreateView, FinishView, OrderListView, OrderDetailView, OrderUpdateView, OrderDeleteView

app_name = 'Order_System'
urlpatterns = [
    path('', OrderCreateView.as_view() , name = 'Odrer'),
    path('finish/', FinishView.as_view() , name = 'finish'),
    path('orderlist/', OrderListView.as_view(), name = 'order_list'),
    path('detail/<str:pk>', OrderDetailView.as_view(), name = 'orderdetail'),
    path('Update/<str:pk>', OrderUpdateView.as_view(), name = 'OrderUpdate'),
    path('Delete/<str:pk>', OrderDeleteView.as_view(), name = 'OrderDelete')
]

account_info_confirm_delete.html

和DetailView 一樣若沒設定context_object_name的話,預設是object
我們就可以指定pk 來做刪除

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form method="POST">
        {% csrf_token %}
        <h1>你確定要刪除{{object.account_name}}的資料嗎</h1>
        <input type = "submit" value = "確認">
    </form>
    
</body>
</html>

呈現畫面

當進到這個頁面時就是準備要進行刪除
按下確認後會跳轉回預約頁面

https://ithelp.ithome.com.tw/upload/images/20221013/2015092779BuQPioRz.png

https://ithelp.ithome.com.tw/upload/images/20221013/20150927jYUhXF2B5f.png

我們再回去DetailView頁面找小明時,會發現已經找不到了!

https://ithelp.ithome.com.tw/upload/images/20221013/20150927ds195gC7a9.png

因為要時間剩的不多,所以把兩天的內容濃縮成一天,因為專注在新內容就沒有太多時間檢查,如果有問題的話可以再跟我說喔!
那我們就明天見了~

參考資料&推薦閱讀

https://docs.djangoproject.com/en/4.1/topics/class-based-views/intro/
https://www.learncodewithmike.com/2020/04/django-class-based-views.html
https://andyludeveloper.medium.com/%E7%8E%A9-django-part-5-generic-view-62f3a6c018cd


上一篇
Day- 27 - ModelForm - 實現 Django Form 和 Model 的連結
下一篇
Day -29 - auth - Django內建的超好用註冊和登入
系列文
從0 到 50 初探 如何使用Django 架構出一個網站30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言