1. 程式人生 > >Django實戰(一)-----用戶登錄與註冊系統4(表單)

Django實戰(一)-----用戶登錄與註冊系統4(表單)

表數據 containe 組織 字段類型 wid 所有 redirect 用戶輸入 發送

我們前面都是手工在HTML文件中編寫表單form元素,然後在views.py的視圖函數中接收表單中的用戶數據,再編寫驗證代碼進行驗證,最後使用ORM進行數據庫的增刪改查。這樣費時費力,整個過程比較復雜,而且有可能寫得不太恰當,數據驗證也比較麻煩。

設想一下,如果我們的表單擁有幾十上百個數據字段,有不同的數據特點,如果也使用手工的方式,其效率和正確性都將無法得到保障。

有鑒於此,Django在內部集成了一個表單功能,以面向對象的方式,直接使用Python代碼生成HTML表單代碼,專門幫助我們快速處理表單相關的內容。

Django的表單給我們提供了下面三個主要功能:

  • 準備和重構數據用於頁面渲染;
  • 為數據創建HTML表單元素;
  • 接收和處理用戶從表單發送過來的數據。

編寫Django的form表單,非常類似我們在模型系統裏編寫一個模型。

在模型中,一個字段代表數據表的一列,而form表單中的一個字段代表<form>中的一個<input>元素。

一、創建表單模型

在項目根目錄的login文件夾下,新建一個forms.py文件,也就是/login/forms.py,又是我們熟悉的Django組織文件的套路,一個app一套班子!

/login/forms.py中寫入下面的代碼,是不是有一種編寫數據model模型的既視感?

from django import forms


class UserForm(forms.Form):
    username = forms.CharField(label="用戶名", max_length=128)
    password = forms.CharField(label="密碼", max_length=256, widget=forms.PasswordInput)

說明:

  • 要先導入forms模塊
  • 所有的表單類都要繼承forms.Form類
  • 每個表單字段都有自己的字段類型比如CharField,它們分別對應一種HTML語言中<form>內的一個input元素。這一點和Django模型系統的設計非常相似。
  • label參數用於設置<label>標簽
  • max_length限制字段輸入的最大長度。它同時起到兩個作用,一是在瀏覽器頁面限制用戶輸入不可超過字符數,二是在後端服務器驗證用戶輸入的長度也不可超過。
  • widget=forms.PasswordInput用於指定該字段在form表單裏表現為<input type=‘password‘ />
    ,也就是密碼輸入框。

二、修改視圖

使用了Django的表單後,就要在視圖中進行相應的修改:

from django.shortcuts import render
from django.shortcuts import redirect
from . import models
from django import forms
from login import forms

def index(request):
    pass
    return render(request, ‘login/index.html‘)


def login(request):
    if request.method == "POST":
        login_form = forms.UserForm(request.POST)
        message = "請檢查填寫的內容!"
        if login_form.is_valid():
            username = login_form.cleaned_data[‘username‘]
            password = login_form.cleaned_data[‘password‘]
            try:
                user = models.User.objects.get(name=username)
                if user.password == password:
                    return redirect(‘/index/‘)
                else:
                    message = "密碼不正確!"
            except:
                message = "用戶不存在!"
        return render(request, ‘login/login.html‘, locals())

    login_form = forms.UserForm()
    return render(request, ‘login/login.html‘, locals())


def register(request):
    pass
    return render(request, ‘login/register.html‘)


def logout(request):
    pass
    return redirect("/index/")

說明:

  • 對於非POST方法發送數據時,比如GET方法請求頁面,返回空的表單,讓用戶可以填入數據;
  • 對於POST方法,接收表單數據,並驗證;
  • 使用表單類自帶的is_valid()方法一步完成數據驗證工作;
  • 驗證成功後可以從表單對象的cleaned_data數據字典中獲取表單的具體值;
  • 如果驗證不通過,則返回一個包含先前數據的表單給前端頁面,方便用戶修改。也就是說,它會幫你保留先前填寫的數據內容,而不是返回一個空表!

另外,這裏使用了一個小技巧,Python內置了一個locals()函數,它返回當前所有的本地變量字典,我們可以偷懶的將這作為render函數的數據字典參數值,就不用費勁去構造一個形如{‘message‘:message, ‘login_form‘:login_form}的字典了。這樣做的好處當然是大大方便了我們,但是同時也可能往模板傳入了一些多余的變量數據,造成數據冗余降低效率。

三、 修改login頁面

Django的表單很重要的一個功能就是自動生成HTML的form表單內容。現在,我們需要修改一下原來的login.html文件:

{% extends ‘login/base.html‘ %}
{% load staticfiles %}
{% block title %}登錄{% endblock %}
{% block css %}<link href="{% static ‘css/login.css‘ %}" rel="stylesheet"/>{% endblock %}


{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
          <form class=‘form-login‘ action="/login/" method="post">

              {% if message %}
                  <div class="alert alert-warning">{{ message }}</div>
              {% endif %}
              {% csrf_token %}
              <h2 class="text-center">歡迎登錄</h2>

              {{ login_form }}

              <button type="reset" class="btn btn-default pull-left">重置</button>
              <button type="submit" class="btn btn-primary pull-right">提交</button>

          </form>
        </div>
    </div> <!-- /container -->
{% endblock %}

說明:

  • 你沒有看錯!一個{{ login_form }}就直接完成了表單內容的生成工作!login_form這個名稱來自你在視圖函數中生成的form實例的變量名!
  • 但是,它不會生成<form>...</form>標簽,這個要自己寫;
  • 使用POST的方法時,必須添加{% csrf_token %}標簽,用於處理csrf安全機制;
  • Django自動為每個input元素設置了一個id名稱,對應label的for參數
  • 重置和提交按鈕需要自己寫,Django不會幫你生成!

我們到瀏覽器中,看下實際生成的html源碼是什麽:

<form class=‘form-login‘ action="/login/" method="post">


    <div class="alert alert-warning">密碼不正確!</div>
    <input type=‘hidden‘ name=‘csrfmiddlewaretoken‘ value=‘t7MdqJzR7fbiDth5ZQSBpHb22F8sUkjTy32MlEuhXdW8EZPTwcTNuF0PPOHlxKPz‘ />
    <h2 class="text-center">歡迎登錄</h2>

    <tr><th><label for="id_username">用戶名:</label></th><td><input type="text" name="username" value="jack" maxlength="128" required id="id_username" /></td></tr>
    <tr><th><label for="id_password">密碼:</label></th><td><input type="password" name="password" maxlength="256" required id="id_password" /></td></tr>

      <button type="reset" class="btn btn-default pull-left">重置</button>
      <button type="submit" class="btn btn-primary pull-right">提交</button>

</form>

也就是說,Django的form表單功能,幫你自動生成了下面部分的代碼:

<tr><th><label for="id_username">用戶名:</label></th><td><input type="text" name="username" value="jack" maxlength="128" required id="id_username" /></td></tr>
    <tr><th><label for="id_password">密碼:</label></th><td><input type="password" name="password" maxlength="256" required id="id_password" /></td></tr>

這看起來好像一個<table>標簽啊?沒錯,就是<table>標簽,而且是不帶<table></table>的,捂臉!

實際上除了通過{{ login_form }}簡單地將表單渲染到HTML頁面中了,還有下面幾種方式:

  • {{ login_form.as_table }} 將表單渲染成一個表格元素,每個輸入框作為一個<tr>標簽
  • {{ login_form.as_p }} 將表單的每個輸入框包裹在一個<p>標簽內
  • {{ login_form.as_ul }} 將表單渲染成一個列表元素,每個輸入框作為一個<li>標簽

註意:上面的渲染方法中都要自己手動編寫<table>或者<ul>標簽。

重新啟動服務器,刷新頁面,如下圖所示:

技術分享圖片

四、手動渲染表單字段

直接{{ login_form }}雖然好,啥都不用操心,但是界面真的很醜,往往並不是你想要的,如果你要使用CSS和JS,比如你要引入Bootstarps框架,這些都需要對表單內的input元素進行額外控制,那怎麽辦呢?手動渲染字段就可以了。

可以通過{{ login_form.name_of_field }}獲取每一個字段,然後分別渲染,如下例所示:

<div class="form-group">
  {{ login_form.username.label_tag }}
  {{ login_form.username}}
</div>
<div class="form-group">
  {{ login_form.password.label_tag }}
  {{ login_form.password }}
</div>

其中的label標簽可以用label_tag方法來生成。這樣子更加靈活了,但是靈活的代價就是我們要寫更多的代碼,又偏向原生的HTML代碼多了一點。

但是問題又...又...又來了!刷新登錄頁面,卻是下圖的樣子:

技術分享圖片

好像Bootstrap沒有生效呀!仔細查看最終生成的頁面源碼,你會發現,input元素裏少了一個CSS的類form-control。這可咋辦?

在form類裏添加attr屬性即可,如下所示修改login/forms.py

from django import forms

class UserForm(forms.Form):
    username = forms.CharField(label="用戶名", max_length=128, widget=forms.TextInput(attrs={‘class‘: ‘form-control‘}))
    password = forms.CharField(label="密碼", max_length=256, widget=forms.PasswordInput(attrs={‘class‘: ‘form-control‘}))

再次刷新頁面,我們熟悉的Bootstarp框架UI又回來了!

實際上,Django針對{{ login_form }}表單,提供了很多靈活的模板語法,可以循環,可以取值,可以針對可見和不可見的部分單獨控制,詳細內容可以參考教程前面的章節。

技術分享圖片

Django實戰(一)-----用戶登錄與註冊系統4(表單)