[Python+Django]初心者筆記12(form表單介紹 part2:model form基本作法)
modelform表單上顯示的欄位的建立的基本原則就是利用models.py定義的資料表欄位來產生
相較於之前的文章建立的book_renew_librarian.html,其實程式碼差異不大,但是當網頁上的欄位非常多
且無須自行實做每個欄位的驗證的時候,利用modelform的方式來建立表單,就不用逐一在forms.py定義所需要的欄位
以下就來示範modelform來實做book_renew_librarian的方式!每個步驟都跟前篇文章
[Python+Django]初心者筆記11(form表單介紹 part1)
https://dotblogs.com.tw/kevinya/2018/07/19/143507
做出來的book_renew_librarian非常相似!其中有差異的部分非常少,註解都有把差異的部分標示出來
首先我們在catalog/templates/catalog/bookinstance_list_borrowed_all.html加上另一個超連結RenewByModelForm,
用來跟原本的超連結Renew做一個區別:
- <a href="{% url 'renew-book-librarian-modelform' bookinst.id %}">RenewByModelForm</a>
再來新增一個html檔:locallibrary\catalog\templates\catalog\book_renew_librarian_modelform.html:
{% extends "base_generic.html" %}
{% block content %}
<!-- book_inst的由來是views.py -->
<h1>Renew: {{bookinst.book.title}}</h1>
<p>Borrower: {{bookinst.borrower}}</p>
<p{% if bookinst.is_overdue %} class="text-danger"{% endif %}>Due date: {{bookinst.due_back}}</p>
<form action="" method="post">
{% csrf_token %}
<table>
<!-- forms.py定義的欄位將會自動在下面的form變數裡面產生 -->
{{ form }}
</table>
<input type="submit" value="Submit" />
</form>
<!-- 讓畫面上的date欄位有可愛的UI可以快速選取日期 -->
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="/resources/demos/style.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$( function() {
//把好用的datepicker元件,套用在新的欄位名稱id_due_back
$( "#id_due_back" ).datepicker( { dateFormat: 'yy-mm-dd' } );
} );
</script>
{% endblock %}
然後於locallibrary\catalog\urls.py額外定義另一個url mapping:
#圖書館管理人員限定的 更新讀者書本到期日的功能(改用modelform的方式實做)
#網址格式:/catalog/book/<bookinstance id>/renew_bymodelform/
#renew_book_librarian是底線分隔,表示這是一個function-based view
urlpatterns += [
path('book/<uuid:pk>/renew_bymodelform/', views.renew_book_librarian_modelform, name='renew-book-librarian-modelform'),
]
再來於locallibrary\catalog\forms.py最下面加上這段以modelform方式實做的欄位驗證:
#利用modelform快速建立create,delete,update功能
#類似asp.net MVC的skeleton
from django.forms import ModelForm
from .models import BookInstance
class RenewBookModelForm(ModelForm):
#clean_OOXX就是用來驗證這個欄位的
#ps.OOXX必須為models.py定義的欄位名稱
def clean_due_back(self):
data = self.cleaned_data['due_back']
#Check date is not in past.
if data < datetime.date.today():
raise ValidationError(_('Invalid date - renewal in past(model form)'))
#Check date is in range librarian allowed to change (+4 weeks)
if data > datetime.date.today() + datetime.timedelta(weeks=4):
raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead(model form)'))
# Remember to always return the cleaned data.
return data
class Meta:
model = BookInstance
#下面這是modelform指定單一欄位的方式
#另有1. fields = '__all__' 或 exclude 的寫法,因此欄位數量很多時,用modelform實做較為方便
fields = ['due_back']
#複寫due_back的label名稱
labels = { 'due_back': _('Renewal date'), }
help_texts = { 'due_back': _('Enter a date between now and 4 weeks (default 3).'), }
最後在locallibrary\catalog\views.py定義server side的行為:
#renew_book_librarian用於讀書館員幫讀者手動更新書的到期日
#****************改用modelform的方式實做!***********
from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse
import datetime
#用modelform方式實做的話,與之前的差異點:需改 import RenewBookModelForm
#from .forms import RenewBookForm
from .forms import RenewBookModelForm
def renew_book_librarian_modelform(request, pk):
book_inst=get_object_or_404(BookInstance, pk = pk)
if request.method == 'POST':
#用modelform方式實做的話,與之前的差異點:需改用RenewBookModelForm去做欄位驗證
#form = RenewBookForm(request.POST)
form = RenewBookModelForm(request.POST)
# Check if the form is valid:
if form.is_valid():
#用modelform方式實做的話,與之前的差異點:欄位名稱需改成due_back
#book_inst.due_back = form.cleaned_data['renewal_date']
book_inst.due_back = form.cleaned_data['due_back']
book_inst.save()
return HttpResponseRedirect(reverse('all-borrowed') )
else:
proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
#用modelform方式實做的話,與之前的差異點:欄位名稱需改成due_back
# form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
form = RenewBookModelForm(initial={'due_back': proposed_renewal_date,})
return render(request, 'catalog/book_renew_librarian_modelform.html', {'form': form, 'bookinst':book_inst})
這樣子就可以了,畫面上看起來除了下面的超連結跟之前不一樣之外,其他的功能都完全相同,只是實做方式改用modelform的方式而已:
參考資料:
[Python+Django]初心者筆記11(form表單介紹 part1)
https://dotblogs.com.tw/kevinya/2018/07/19/143507
Django Tutorial Part 9: Working with forms
https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Forms