服務端編程(十三)- Django - forms 表單的使用

前言 ´・ᴗ・`

  • 所謂表單 就是一些要我們填寫的表格 比方註冊網站的登記表
  • 本篇帶你用Django 玩玩表單
  • 本篇內容將會幫助你學習…
    • 1 HTML表單的一般處理方式
    • 2 Django forms類 表單處理方式 使用方法
    • 3 爲圖書館應用創造一個 續借renew 的表單

HTML表單結構

我們看個簡單的代碼

<form action="/team_name_url/" method="post">
    <label for="team_name">Enter name: </label>
    <input id="team_name" type="text" name="name_field" value="Default name for team.">
    <input type="submit" value="OK">
</form>
  • action 就是 我們用戶填完表 submit提交以後 我們提交的url地址
  • method 就是 請求類型 是get還是post
    • get 適合 不更改數據庫的表單 比如 搜索框
    • post 適合 更改數據庫的表單 比如註冊 登錄 錄入個人信息等

處理表單的流程(Django處理的流程)

  1. 顯示默認表單 編輯框裏的東西 有時稱爲佔位符placeholder
    在這裏插入圖片描述
    此時表單被稱爲未綁定(un bind) 沒有任何來自用戶的信息注入 綁定啥呢?
  2. 提交請求 服務器接收數據,並將其綁定到表單。
    這時 數據綁定到表單了
  3. 檢查 淨化數據 然後從數據中提取必要信息 轉換成python對象 比如日期對象
    所謂檢查內容 避免類似XSS攻擊的惡意代碼 刪除可能用於向服務器發送惡意內容的無效字符
  4. 根據數據庫模型 對數據進行限制 (例如,在正確的日期範圍內,不是太短或太長等)
    因爲前面已經把數據弄成對象了 現在比較值 限制什麼都很簡單
    當然現在簡單的限制數據檢查 是讓前端來幹了 分擔服務器工作
    複雜的檢查一般是AJAX 也就動態加載 對錶單信息的驗證結果 而不是重新刷新頁面(太low了?)
  5. 驗證檢查值是否適合該字段
    • 如果數據無效,重新顯示錶單
    • 如果數據都有效,執行必要的操作(例如保存數據,發送表單和發送電子郵件,返回搜索結果,上傳文件等)
  6. 完成所有操作後,將用戶重定向到另一個頁面

Forms

Django提供了Forms 這個工具來封裝這些表單
其思維是 既然數據最終走向的是數據庫
那麼語法也應該接近ORM的寫法
我們看例子就懂了

from django import forms
    
class RenewBookForm(forms.Form):
    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")

除了DateField 還有其他的 如:
BooleanField, CharField, ChoiceField, TypedChoiceField, DateField, DateTimeField, DecimalField, DurationField, EmailField, FileField, FilePathField, FloatField, ImageField, IntegerField, GenericIPAddressField, MultipleChoiceField, TypedMultipleChoiceField, NullBooleanField, RegexField, SlugField, TimeField, URLField, UUIDField, ComboField, MultiValueField, SplitDateTimeField, ModelMultipleChoiceField, ModelChoiceField​​​​.

然而我覺得 用到的時候再說是最好的方式

這些field有些共有屬性

  • required: 如果爲True,則該字段不能爲空
  • label: 在 HTML 中呈現字段時使用的標籤。如果未指定label,則 Django 將通過大寫第一個字母、並用空格替換下劃線(例如續訂日期)的方式,從字段名稱創建一個。
  • label_suffix: django連編輯框前的說明文字都包了2333
    在這裏插入圖片描述
    默認情況下,標籤後面會顯示冒號(例如續借日期: )。此參數允許您指定包含其他字符的不同後綴。
  • initial: 顯示錶單時,字段的初始值 就是佔位符的意思
  • widget: 要使用的顯示小部件(這個後面會講是什麼“小部件”)
  • help_text :可以在表單中顯示的附加文本,用於說明如何使用該字段。
  • error_messages: 字段的錯誤消息列表。如果需要,您可以使用自己的消息,覆蓋這些消息。
  • validators: 驗證時 調用的驗證函數列表
  • localize: 啓用表單數據輸入的本地化 就是語言的問題啦
  • disabled: 如果爲True 無法編輯其值 禁用狀態

這裏 對於我們圖書館應用 我們就 設定一個 續借的表單好了
我們命名爲“renewal_date” 作爲 續借日期的字段field 名字

驗證數據

我們想驗證數據 可以採用 覆蓋原有的驗證函數來實現
在view class 視圖那邊 我們可以這麼寫 locallibrary/catalog/forms.py:

from django import forms

from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
import datetime #for checking renewal date range.
    
class RenewBookForm(forms.Form):
    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")

    def clean_renewal_date(self):
        data = self.cleaned_data['renewal_date']
        
        #檢測日期是不是過去的日期
        if data < datetime.date.today():
            raise ValidationError(_('Invalid date - renewal in past'))

        #檢測 還書日期是否在1個月內
        if data > datetime.date.today() + datetime.timedelta(weeks=4):
            raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))

        # Remember to always return the cleaned data.
        return data
  • 注意命名 clean_renewal_date 驗證函數都是clean_<字段名稱> 這種格式的
  • 注意 cleaned_data 函數 這玩意太強了 直接淨化數據中那些不合法字段(比如惡意代碼之類的),獲取到有用的信息 然後轉成合法的python對象 這裏就是date日期對象
  • ValidationError 這玩意就是說 驗證不通過 然後彈出錯誤信息給客戶看 用_() 函數

搞定form的設置 我們添加url:locallibrary/catalog/urls.py

 path('book/<uuid:pk>/renew/', views.renew_book_librarian, name='renew-book-librarian'),

這句話就是 匹配form action填寫的url地址 利用uuid格式 提取關鍵值(在這裏 就是書的id)
然後存到pk(primary key縮寫)的變量中去
然後我們用view的函數renew_book_librarian處理提交上來的數據

view處理提交的數據

上面把鍋甩給了view 這裏我們view就要面對現實了
大致思路就是區分method 是POST還是GET?兩種上傳數據的方式帶來兩種不同的處理方式
上代碼:

from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse
import datetime

from .forms import RenewBookForm

def renew_book_librarian(request, pk):
	#首先找到書的實例(我們是續借具體的書 書都找不到還續借啥?)
    book_inst=get_object_or_404(BookInstance, pk = pk)
    
	# 如果是POST方法
    if request.method == 'POST':
        # 用POST解析 然後用form對象去給數據建模 也就是格式化 結構化數據
        form = RenewBookForm(request.POST)
		
		#然後調用 clean_renewal_date()
        if form.is_valid():
          
            book_inst.due_back = form.cleaned_data['renewal_date']
            book_inst.save()

            # redirect to a new URL:
            return HttpResponseRedirect(reverse('all-borrowed') )

    # If this is a GET (or any other method) create the default form.
    else:
        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})

    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})
  • RenewBookForm 這裏 首先是 這個就是我們之前forms.py 那個類
    這裏涉及幾個概念
    一個是 這就是典型的 結構化 也就是裸數據弄成python對象以後 讓對比值 限制值 so easy
    第二是 這也是稱爲“綁定”binding數據操作

  • return HttpResponseRedirect(reverse(‘all-borrowed’) )
    這就是之前說的 表單成功提交 數據ok的話 我們重定向到另一個頁面 (受view管理的當然)

  • get_object_or_404(): 根據模型的主鍵值,從模型返回指定的對象,如果記錄不存在,則引發Http404 異常(未找到)。

  • HttpResponseRedirect: 這將創建指向指定URL的重定向(HTTP狀態代碼 302)。

  • reverse(): 反向定位 之前也講過 通過映射的名字“all-borrow” 得到url值 然後作爲參數讓HTTP重定向

然後 如果GET方式 或者POST數據不合法 一樣render打包數據回去 送你一個頁面

加上上一節 我們限定了 只有圖書管理員纔能有的權限“set_book_as_returned”
所以對於這個“all borrow”頁面 我們可以加裝飾器來限定:

@permission_required('catalog.can_mark_returned')

模板編輯

/catalog/templates/catalog/book_renew_librarian.html

{% extends "base_generic.html" %}
{% block content %}

    <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>
        {{ form }}
        </table>
        <input type="submit" value="Submit" />
    </form>

{% endblock %}

這裏注意:在表單標籤內添加的{% csrf_token %} ,是 Django 跨站點僞造(XSS攻擊)保護的一部分。

然後 我們再簡單限制上一節 有關圖書管理員的管理頁面 “續借按鈕”的設計 bookinstance_staff_management.html

{% extends "base_generic.html" %}

{% block content %}
    <h1>Borrowed books</h1>

    {% if bookinstance_list %}
    <ul>

      {% for bookinst in bookinstance_list %}
      <li class="{% if bookinst.is_overdue %}text-danger{% endif %}">
        <a href="{% url 'book-detail' bookinst.book.pk %}">{{bookinst.book.title}}</a> ({{ bookinst.due_back }}) - {{bookinst.borrower}} - {% if perms.catalog.set_book_as_returned %}- <a href="{% url 'renew-book-librarian' bookinst.id %}">Renew</a>  {% endif %}
      </li>
      {% endfor %}
    </ul>

    {% else %}
      <p>There are no books borrowed.</p>
    {% endif %}
{% endblock %}

總結 ´◡`

runserver http://127.0.0.1:8000/catalog/borrowed/

在這裏插入圖片描述
在這裏插入圖片描述
invalidate input:
在這裏插入圖片描述
下一節我們簡要複習並拓展之前學習的內容 完善我們的圖書館應用
服務端編程(十四)- Django - 視圖 模板設計的補充

我的專欄 希望能夠幫到你 ( •̀ ω •́ )✧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章