Python Django 學習紀錄(五) 資料新增、刪除和修改
一、表單資料的傳送與接收:
1.表單傳送資料的方法
在頁面中建置表單區域,區域內放置填寫資料的表單元件,按下表單中的submit送出資料,頁面會將表單區域中的資料傳定的目標網頁接收處理。
<form> 標籤所包圍的區域為表單區域,action 屬性指定傳送的目標頁面,method 屬性即是傳送的方式,又分為GET跟POST兩種
- GET :表單資料將以字串的方式附加在網址後面傳送
- POST :表單資料將放置在HTTP標頭的方式傳送
2.表單接收資料的方法
在<views.py>視圖中的函式,第一個參數必須是HttpRequest型別物件(預設名稱為 request),它包含頁面請求和使用者資訊。
request提供三種方法:
- GET:取得以GET方式傳送的表單欄位內容
- POST:取得以POST方式傳送的表單欄位內容
- method:取得表單的傳送方式
這裡將延續使用 學習紀錄(四) 的 students 專案
首先在 <urls.py> 定義下列的 URL:
先將還不會用到的路徑註解掉,之後要使用時再陸續打開,以免之後運行伺服器會產生找不到路徑中函數的問題:
from django.contrib import admin from django.urls import path,re_path from studentsapp import views urlpatterns = [ path('admin/', admin.site.urls), path('listone/',views.listone), path('listall',views.listall), path('',views.index), path('index/',views.index), path('post/',views.post), #POST傳送表單 # path('post1/',views.post1), #資料新增,資料不驗證 # path('post2/',views.post2), #資料新增,資料作驗證 # re_path(r'delete/(\d+)/$',views.delete), # re_path(r'edit/(\d+)/$',views.edit), # re_path(r'edit/(\d+)/(\w+)$',views.edit), #由 瀏覽器開啟 # re_path(r'edit2/(\d+)/(\w+)$',views.edit2), # path('postform',views.postform), #表單驗證 ]
範例1.使用POST方式傳送資料
欲達成目標:
- 佈置一個簡單的表單,使用POST的方式傳送資料到目標網頁接收顯示
請打開studentsapp裡的<view.py>寫一個名為post的自訂函數
def post(request): if request.method == "POST": #如果是以POST的方式才處理 mess = request.POST['username'] #取得表單輸入資料 else: mess = "表單資料尚未送出!" return render(request,"post.html",locals())
接著在 templates目錄中創建一個<post.html>模版
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>表單傳送範例</title> </head> <body> <form action="/post" method="POST" name="form1"> {% csrf_token %} <!-- 啟動CSRF防護,保護伺服器避免被攻擊 --> <div>請輸入姓名:<input type="text" name="username"> <input type="submit" value="送出資料"> </div> <div>接收到的資料:{{mess}}</div> </form> </body> </html>
最後記得在在<urls.py>新增post的url路徑(或是去掉之前的註解)
網頁執行結果如下:
輸入好資料後,點選送出資料
※範例.整合資料目錄
接下來將會做「新增、修改、刪除」的動作,而這些都將反映在最後的目錄上,所以先將整合的目錄先寫出來,再來才做新增修改刪除的動作。
對於之前的<index.html>相比而言,加入了許多與其他頁面的連結。
※按下編輯二按鈕時以「"/edit2/{{student.id}}/load"」傳送id和load參數,load字串表示他是由<index.html>傳送過來的。
打開在 templates目錄中的<index.html>模版,這將是所有程式最後指向的目錄:
{% extends 'base.html' %} {% block title %} <title>顯示所有資料</title>{% endblock %} {% block content %} <h2 align="left"> 顯示 student 資料表所有資料 <a href="/post1/">新增一</a> <a href="/post2/">新增二</a> </h2> <table border="1" cellpadding="0" cellspacing="0"> <th>編號</th> <th>姓名</th> <th>性別</th> <th>生日</th> <th>郵件帳號</th> <th>電話</th> <th>地址</th> {% for student in students %} <tr> <td>{{student.id}} </td> <td>{{student.cName}} </td> <td>{{student.cSex}} </td> <td>{{student.cBirthday}} </td> <td>{{student.cEmail}} </td> <td>{{student.cPhone}} </td> <td>{{student.cAddr}} </td> <td> <a href="/edit/{{student.id}}" title="編輯一">編輯一</a> <a href="/edit/{{student.id}}/load" title="編輯二">編輯二</a> <a href="/delete/{{student.id}}" title="刪除">刪除</a> </td> </tr> {% endfor %} </table> {% endblock %}
二、資料新增:
1.資料新增和儲存
利用 objects.create方法可以新增一筆資料,語法:
紀錄 = 資料表.object.create(欄位 1= 內容 1,...)
: 以save方法可以將該筆紀錄寫入資料庫
unit.save()
範例2.資料新增
欲達成目標:
- 將表單資料和資料庫結合,在表單輸入資料後,以POST方式傳送資料到資料庫建立一筆紀錄
請打開studentsapp裡的<view.py>寫一個名為post1的自訂函數
這裡會使用到redirect套件,所以要導入該套件 from django.shortcuts import render,redirect
def post1(request): if request.method == "POST":#如果是以POST的方式才處理 cName = request.POST['cName'] #取得表單輸入資料 cSex = request.POST['cSex'] cBirthday = request.POST['cBirthday'] cEmail = request.POST['cEmail'] cPhone = request.POST['cPhone'] cAddr = request.POST['cAddr'] unit = student.objects.create(cName=cName, cSex=cSex, cBirthday=cBirthday, cEmail=cEmail, cPhone=cPhone, cAddr=cAddr) unit.save() #寫入資料庫 return redirect('/index/') #重導到<index.html>網頁 else: message = '請輸入資料(資料不作驗證)' return render(request, "post1.html", locals())
接著在 templates目錄中創建一個<post1.html>模版
{% extends 'base.html' %} {% block title %} <meta charset="utf-8"> <title>資料表新增(一)</title> {% endblock %} {% block content %} <h2>資料表新增(一)</h2> <form action="." method="POST" name="form1"> <!-- "."表示執行相同的url --> {% csrf_token %} #CSRF防護請加在此 <table border="1" cellpadding="1" cellspacing="4"> <tr> <th>姓名</th> <td><input type="text" id="cName" name="cName" /></td> </tr> <tr> <th>姓別</th> <td> <input type="radio" id="cSex1" name="cSex" value="M" checked="checked"/>男生 <input type="radio" id="cSex2" name="cSex" value="F"/>女生 </td> </tr> <tr> <th>生日</th> <td><input type="text" id="cBirthday" name="cBirthday" /></td> </tr> <tr> <th>郵件帳號</th> <td><input type="text" id="cEmail" name="cEmail" /></td> </tr> <tr> <th>電話</th> <td><input type="text" id="cPhone" name="cPhone" /></td> </tr> <tr> <th>地址</th> <td><input type="text" id="cAddr" name="cAddr" /></td> </tr> <tr> <th colspan="2" align="center"> <input type="submit" name="button" id="button" value="送出"> <input type="reset" name="button2" id="button2" value="重設"> </tr> </table> <span style="color:red">{{message}}</span> </form> {% endblock %}
最後記得在<urls.py>新增post1的url路徑(或是去掉之前的註解)
網頁執行結果如下:
新增後結果如下:
※編號為6是因為我刪除兩筆資料做過測試,所以這邊再新增直接從6開始
三、表單模型化:
若輸入的資料並未作驗證,很容易引發不可預期的錯誤,Django提供很好的表單驗證,可以有效的驗證資料格式是否正確。
1.建立表單模型
首先必須在 studentsapp 目錄下新增一個 <form.py> 檔案,然後建立繼承forms.Form的類別,格式與Mode的格式相似
max_length可設定最大長度,initial設定初始值,required設定資料是否可選填,預設值True表示資料不可空白
以下將建立一個名為PostForm類別的表單模型
from django import forms #導入form套件 class PostForm(forms.Form): #建立類別,需繼承 forms.Form cName = forms.CharField(max_length=20,initial='') cSex = forms.CharField(max_length=2,initial='M') cBirthday = forms.DateField() cEmail = forms.CharField(max_length=100,initial='',required=False) cPhone = forms.CharField(max_length=20,initial='',required=False) cAddr = forms.CharField(max_length=20,initial='',required=False)
2.建立表單模型物件
利用表單模型就可以建立表單物件,例如以PostForm類別建立空白物件:
postform = PostForm()
也可以字典建立含有初始值的物件。
而因為request.POST可以取得form表單以POST方式傳送的資料,最適合直接用來建立表單物件,這樣postform初始值就是表單傳送的資料:
postform = PostForm(request.POST)
3.輸出表單
輸出表單的部分將在下面的範例直接示範說明。
範例3.基本表單驗證
欲達成目標:
- 利用表單模型物件,建立基本的表單驗證
請打開studentsapp裡的<view.py>寫一個名為postform的自訂函數,並將其中的postform物件傳送給<postform.html>樣板
from studentsapp.form import PostForm #導入form模型的PostForm類別 def postform(request): postform = PostForm() #建立postform物件 return render(request,"post.html",locals())
接著在 templates目錄中創建一個<postform.html>模版
{% extends "base.html" %} {% block title %}<title>基本表單驗證</title>{% endblock %} {% block content %} <h2>基本表單驗證</h2> <form action="." method="POST" name="form1"> {% csrf_token %} <table border="1" cellspacing="1" cellpadding="4"> {{postform}} <!-- {{postform.as_p}} #段落模式 --> <!-- {{postform.as_ul}} #列表格式 --> </table> </form> {% endblock %}
最後記得在<urls.py>新增postform的url路徑(或是去掉之前的註解)
網頁執行結果如下:
4.表單樣式客製化
以單一欄位輸出的表單格式可以搭配css樣式製作個人風格,例如:
<p class="css1">姓名:{{postform.cName}}</p>
5.表單驗證
前面已經有提到,在表單輸入欄位的設定,required 表示資料是否可選填,預設值True表示資料不可空白 ,則輸入的資料就必須符合其型別驗證,例如type=Email的輸入資料就必須符合Email格式。若值為False則表示資料可以空白。
可以用 is_vaild()方法檢查輸入資料是否通過驗證,通過驗證後就可以cleaned_data取得表單輸入的資料,例如:
if postform.is_vaild(): cName = postform.cleaned_data['cName']
範例4.資料新增並驗證
欲達成目標:
- 在表單中輸入資料後,以POST方式傳送到資料庫建立一筆紀錄,輸入資料必須作驗證
請打開studentsapp裡的<view.py>寫一個名為post2的自訂函數
- 判斷是否以POST方式送出
- 檢查表單是否通過form驗證
- 通過驗證的話就以cleaned_data取得輸入資料,並將其寫入資料庫
def post2(request): #新增資料,資料必須驗證 if request.method == "POST": #如果是以POST方式才處理 postform = PostForm(request.POST) #以form表單以POST方式傳入的資料為初始值 if postform.is_valid(): #檢查是否通過forms驗證 cName = postform.cleaned_data['cName'] #取得表單輸入資料 cSex = postform.cleaned_data['cSex'] cBirthday = postform.cleaned_data['cBirthday'] cEmail = postform.cleaned_data['cEmail'] cPhone = postform.cleaned_data['cPhone'] cAddr = postform.cleaned_data['cAddr'] #新增一筆資料 unit = student.objects.create(cName=cName,cSex=cSex,cBirthday=cBirthday,cEmail=cEmail,cPhone=cPhone,cAddr=cAddr) unit.save() #寫入資料庫 message = "已儲存" return redirect('/index/') else: message = "驗證碼錯誤!" #若未通過驗證,顯示此訊息 else: message = "姓名、性別、生日必須輸入!" postform = PostForm() #建立空白的postform物件 return render(request,"post2.html",locals())
接著在 templates目錄中創建一個<post2.html>模版
建立表單,輸入資料後以POST的方式傳送資料並驗證
{% extends 'base.html' %} {% block title %} <meta charset="utf-8"> <title>資料表新增(二)</title> {% endblock %} {% block content %} <h2>資料表新增(二)</h2> <form action="." method="POST" name="form1"> <!-- "."表示執行相同的url --> {% csrf_token %} <!-- #CSRF防護請加在此 --> <table border="1" cellpadding="1" cellspacing="4"> <tr> <th>姓名</th> <td>{{postform.cName}}</td> </tr> <tr> <th>姓別</th> <td> <input type="radio" id="cSex1" name="cSex" value="M" checked="checked"/>男生 <input type="radio" id="cSex2" name="cSex" value="F"/>女生 </td> </tr> <tr> <th>生日</th> <td>{{postform.cBirthday}}</td> </tr> <tr> <th>郵件帳號</th> <td>{{postform.cEmail}}</td> </tr> <tr> <th>電話</th> <td>{{postform.cPhone}}</td> </tr> <tr> <th>地址</th> <td>{{postform.cAddr}}</td> </tr> <tr> <th colspan="2" align="center"> <input type="submit" name="button" id="button" value="送出"> <input type="reset" name="button2" id="button2" value="重設"> </tr> </table> <span style="color:red">{{message}}</span> </form> {% endblock %}
最後記得在<urls.py>新增post2的url路徑(或是去掉之前的註解)
網頁執行結果如下:
※未通過驗證的話會出現提醒輸入的訊息
四、資料刪除:
delete 可以刪除指定的資料,使用 objects 物件的 get all 或 filter方法可取得資料,再以 delete 方法刪除指定的資料
例如刪除 「id = 6」的資料:
id = 6 unit = student.object.get(id=id) unit.delete
範例5.資料刪除
欲達成目標:
- 依據 id 取得該筆資料,再以 delete 刪除該筆資料。
- 若 id 不存在則將顯示頁面讓使用者重新輸入編號
請打開studentsapp裡的<view.py>寫一個名為delete的自訂函數
def delete(request,id=None): #刪除資料 if id!=None: if request.method == "POST": #如果是以POST的分是才處理 id = request.POST('cId') #取得表單輸入的編號 try: unit = student.objects.get(id=id) #取得id欄位的資料 unit.delete() #刪除資料 return redirect('/index/') except: message = "讀取錯誤!" return render(request,"delete.html",locals())
接著在 templates目錄中創建一個<delete.html>模版
建立表單,輸入資料後以POST的方式傳送資料
{% extends "base.html" %} {% load staticfiles %} {% block title %}<title>資料表刪除</title>{% endblock %} {% block content %} <h2>資料表刪除</h2> <form action="." method="POST" name="form1"> {% csrf_token %} <div>請輸入編號:<input type="text" name="cId"></div> <div><input type="submit" name="button" value="確定刪除" /></div> <span style="color: red">{{message}}</span> </form> {% endblock %}
最後記得在<urls.py>新增delete的url路徑(或是去掉之前的註解)
輸入「127.0.0.1:8000/delete/id」後,例如(id=20),會依據 id 刪除指定的資料,或 id 存在就會直接刪除該筆資料,並重新導向到index目錄
若 id 不存在則將顯示以下頁面讓使用者重新輸入編號:
五、資料修改:
利用 save 方法可以將資料寫回資料,如此即可修改資料庫。
例如將編號1的 cName 欄位內容更改為「ivankao」:
unit = student.object.get(id=1) unit.cName = "ivankao" unit.save()
範例6.資料修改(一)
欲達成目標:
- 依據id取得指定的資料,更改資料後按送出鈕,即會更新資料庫中的該筆資料並重導到index目錄頁面。
- 若該id不存在會顯示空白表單並出現「此 id 不存在!」的訊息。
請打開studentsapp裡的<view.py>寫一個名為edit的自訂函數
在這個自訂函數中,要先判斷mode是否為edit字串,若是表示他是由<edit.html>送出的。
後面最做一個轉日期格式的例外處理,例如將「2001年5月18日」轉換為「2001-05-18」
def edit(request,id=None,mode=None): if mode == "edit": #如果是由<edit.html>按submit unit = student.objects.get(id=id) #取得要修改的資料紀錄 unit.cName = request.GET['cName'] #取得表單輸入資料 unit.cSex = request.GET['cSex'] unit.cBirthday = request.GET['cBirthday'] unit.cEmail = request.GET['cEmail'] unit.cPhone = request.GET['cPhone'] unit.cAddr = request.GET['cAddr'] unit.save() message = "已修改" return redirect('/index/') else: #由網址列直接輸入 try: unit = student.objects.get(id=id) #取得要修改的資料紀錄 strdate = str(unit.cBirthday) strdate2 = strdate.replace("年","-") strdate2 = strdate.replace("月","-") strdate2 = strdate.replace("日","-") unit.cBirthday = strdate2 except: message = "此 id 不存在!" return render(request,"edit.html",locals())
接著在 templates目錄中創建一個<edit.html>模版
建立表單,輸入資料後以GET的方式傳送資料
{% extends 'base.html' %} {% block title %} <meta charset="utf-8"> <title>資料表修改(一)</title> {% endblock %} {% block content %} <h2>資料表修改(一)</h2> <form action="/edit/{{unit.id}}/edit" method="GET" name="form1"> <!-- 建立form表單,設定傳送方式為GET,「action="/edit/{{unit.id}}/edit"」中第三個參數為edit,表示他是從<edit.html>按送出鈕傳送過來的--> {% csrf_token %} <!-- #CSRF防護請加在此 --> <table border="1" cellpadding="1" cellspacing="4"> <tr> <th>姓名</th> <td><input type="text" id="cName" name="cName" value="{{unit.cName}}"></td> </tr> <tr> <th>姓別</th> {% if unit.cSex == "M" %} <td> <input type="radio" id="cSex1" name="cSex" value="M" checked="checked"/>男生 <input type="radio" id="cSex2" name="cSex" value="F"/>女生 </td> {% else %} <td> <input type="radio" id="cSex1" name="cSex" value="M"/>男生 <input type="radio" id="cSex2" name="cSex" value="F" checked="checked"/>女生 </td> {% endif %} </tr> <tr> <th>生日</th> <td><input type="text" id="cBirthday" name="cBirthday" value="{{unit.cBirthday}}"></td> </tr> <tr> <th>郵件帳號</th> <td><input type="text" id="cEmail" name="cEmail" value="{{unit.cEmail}}"></td> </tr> <tr> <th>電話</th> <td><input type="text" id="cPhone" name="cPhone" value="{{unit.cPhone}}"></td> </tr> <tr> <th>地址</th> <td><input type="text" id="cAddr" name="cAddr" value="{{unit.cAddr}}"></td> </tr> <tr> <th colspan="2" align="center"> <input type="submit" name="button" id="button" value="送出"> <input type="reset" name="button2" id="button2" value="重設"> </tr> </table> <span style="color:red">{{message}}</span> </form> {% endblock %}
最後記得在<urls.py>新增edit的url路徑(或是去掉之前的註解)
網頁執行結果如下(以編號9為例):
若 id 不存在則會顯示空白頁面與提示訊息:
範例7.資料修改(二)
欲達成目標:
- 在<index.html>頁面二下編輯二連結,依據id取得指定的資料,更改資料後按送出鈕,即會更新資料庫中的該筆資料並重導到index目錄頁面。
請打開studentsapp裡的<view.py>寫一個名為edit2的自訂函數
- 在這個自訂函數中,要先判斷mode是否為load字串,若是表示他是由<index.html>按 編輯二 鈕傳送過來的,則依據id讀取該筆資料。
- 再來將資料傳給<edit2.html>準備編輯,判斷mode是否為save字串,若是表示他是由<edit2.html>按 送出 鈕傳送過來的
def edit2(request,id=None,mode=None): if mode == "load": #如果是由 <index.html> 按 編輯二 鈕 unit = student.objects.get(id=id) #取得要修改的資料紀錄 strdate = str(unit.cBirthday) strdate2 = strdate.replace("年","-") strdate2 = strdate.replace("月","-") strdate2 = strdate.replace("日","-") unit.cBirthday = strdate2 return render(request,"edit2.html",loacls()) elif mode =="save": #如果是由<edit2.html>按submit unit = student.objects.get(id=id) #取得要修改的資料紀錄 unit.cName = request.POST['cName'] #取得表單輸入資料 unit.cSex = request.POST['cSex'] unit.cBirthday = request.POST['cBirthday'] unit.cEmail = request.POST['cEmail'] unit.cPhone = request.POST['cPhone'] unit.cAddr = request.POST['cAddr'] unit.save() message = "已修改" return redirect('/index/')
接著在 templates目錄中創建一個<edit.html>模版
建立表單,輸入資料後POST的方式傳送資料
{% extends 'base.html' %} {% block title %} <meta charset="utf-8"> <title>資料表修改(二)</title> {% endblock %} {% block content %} <h2>資料表修改(二)</h2> <form action="/edit2/{{unit.id}}/save" method="POST" name="form1"> <!-- 建立form表單,設定傳送方式為POST,「action="/edit2/{{unit.id}}/save"」中第三個參數為save,表示他是從<edit2.html>按送出鈕傳送過來的--> {% csrf_token %} <!-- #CSRF防護請加在此 --> <table border="1" cellpadding="1" cellspacing="4"> <tr> <th>姓名</th> <td><input type="text" id="cName" name="cName" value="{{unit.cName}}"></td> </tr> <tr> <th>姓別</th> {% if unit.cSex == "M" %} <td> <input type="radio" id="cSex1" name="cSex" value="M" checked="checked"/>男生 <input type="radio" id="cSex2" name="cSex" value="F"/>女生 </td> {% else %} <td> <input type="radio" id="cSex1" name="cSex" value="M"/>男生 <input type="radio" id="cSex2" name="cSex" value="F" checked="checked"/>女生 </td> {% endif %} </tr> <tr> <th>生日</th> <td><input type="text" id="cBirthday" name="cBirthday" value="{{unit.cBirthday}}"></td> </tr> <tr> <th>郵件帳號</th> <td><input type="text" id="cEmail" name="cEmail" value="{{unit.cEmail}}"></td> </tr> <tr> <th>電話</th> <td><input type="text" id="cPhone" name="cPhone" value="{{unit.cPhone}}"></td> </tr> <tr> <th>地址</th> <td><input type="text" id="cAddr" name="cAddr" value="{{unit.cAddr}}"></td> </tr> <tr> <th colspan="2" align="center"> <input type="submit" name="button" id="button" value="送出"> <input type="reset" name="button2" id="button2" value="重設"> </tr> </table> <span style="color:red">{{message}}</span> </form> {% endblock %}
最後記得在<urls.py>新增edit的url路徑(或是去掉之前的註解)
在<index.html>按下 編輯二 執行結果如下(以編號9為例):