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為例):

謝謝你詳盡的解說
突然發現這個跟我現在再讀的書一模一樣 但是照上面坐一樣有問題,想請問一下資料修改(一)的部分 我在urls.py裡面是打 url(r'^edit/(\d+)/(\w+)$', edit) 是不行的(版本是django1.11.8
如果要使用url方法 在 url.py 中要先導入url的方法 from django.conf.urls import url url(r'edit/(\d+)/$',views.edit), 還有edit前面要指向views 所以後面要改成views.edit 我這邊測試是可行的