Django之ModelForm

  • 2020 年 1 月 23 日
  • 筆記

  在前面有篇部落格,我寫了一個叫forms組件的東西,可以幫助我們完成校驗數據、渲染標籤功能和在前端頁面局部刷新功能,功能封裝的已經很好了,當時已經很開心了。但萬萬沒想到,還有比它功能更強大的東西。forms組件只能渲染出type=text類型的標籤,而且還要我們寫無數多個欄位,然後跟上校驗條件,用forms組件做編輯頁面時,還要手動的把編輯的對象的每一個值寫入標籤的value,這些等等雖然相較沒用forms組件之前更方便了,但跟ModelForm比起來,都顯得蒼白無力,把ModelForm吹了那麼久,現在就讓我們來見識見識ModelForm的強大。

  不管是用什麼,首先都得有模型類吧,創建模型類是沒有變化的,這是往庫里創建表的必有步驟,肯定是變不的。

  一、創建模型類,完成資料庫遷移

  models.py

from django.db import models    # Create your models here.  class Book(models.Model):      name=models.CharField(max_length=15,verbose_name='名字')      price=models.IntegerField(verbose_name='價格')      pub_date=models.DateTimeField(verbose_name='出版時間')      publish = models.ForeignKey('Publish', on_delete=models.CASCADE,verbose_name='出版社')      author=models.ManyToManyField('Author',db_table='book_author',verbose_name='作者')      class Meta:          db_table='Book'          verbose_name='書籍'      def __str__(self):          return self.name  class Publish(models.Model):      name=models.CharField(max_length=15,verbose_name='名字')      addr=models.CharField(max_length=15,verbose_name='地址')      phone=models.IntegerField(verbose_name='電話號碼')      class Meta:          db_table='Publish'          verbose_name='出版社'      def __str__(self):          return self.name  class Author(models.Model):      name=models.CharField(max_length=15,verbose_name='名字')      age=models.IntegerField(verbose_name='年齡')      author_info=models.OneToOneField('Author_Info',on_delete=models.CASCADE,verbose_name='詳情')      class Meta:          db_table='Author'          verbose_name='作者'      def __str__(self):          return self.name  class Author_Info(models.Model):      gf_name=models.CharField(max_length=10,verbose_name='女朋友名字')      telephone=models.IntegerField(verbose_name='電話號碼')      ShenFenZheng=models.IntegerField(verbose_name='身份證號')      class Meta:          db_table='Author_Info'          verbose_name='作者詳情'      def __str__(self):          return str(self.ShenFenZheng)

  二、創建一個ModelForm類

from django import forms  from  django.forms import widgets as wid  class BookModelForm(forms.ModelForm):      class Meta:          model=models.Book             #這相當於給Book模型類創建的          # fields=["title","price"]          # exclude = ["title"]          fields="__all__"             #這是要校驗的欄位,現在表示所有欄位校驗,上面兩種寫法也可以。            widgets={              "name":wid.TextInput(attrs={"class":"form-control"}),              "price":wid.TextInput(attrs={"class":"form-control"})          }                           #這是對用ModelForm生成標籤的屬性修改          error_messages={              "name":{"required":"該欄位不能為空"}          }                          #這是修改錯誤資訊的顯示樣式          labels={              "name":"書籍名稱"          }        def  clean_title(self):       #也可以定義鉤子          val=self.cleaned_data.get("title")          if val.startswith("xxx"):              return val          else:              raise  ValidationError("必須以xxx開頭!")

  ModelForm校驗數據和forms組件是一樣,用is_valid()按照校驗規則來校驗,錯誤的資訊會放在form.errors裡面。

  三、渲染標籤

  views.py

def addbook(request):      if request.method=="POST":          form=BookModelForm(request.POST)          if form.is_valid():              form.save()           #當數據校驗通過後,我們不用寫什麼create,只需要寫上這一句,就完成在表中創建一條記錄。              return redirect("/books/")          else:              return render(request, 'addbook.html', locals())      else:            # form=BookForm()   # forms組件          form=BookModelForm()       #  modelforms組件          return render(request,'addbook.html',locals())

  add.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Title</title>      <link rel="stylesheet" href="/static/css/bootstrap.css">      <script src="/static/jquery-3.3.1.js"></script>      <script src="/static/js/bootstrap.js"></script>      <style>          .outer{              margin-top: 200px;          }          .title{              margin-bottom: 50px;          }          ul{              list-style: none;              margin: 0;              padding: 0;          }          .left{              position: fixed;              left: 0;              top: 20px;          }      </style>  </head>  <body>      <div class="left">          <div class="panel panel-primary">              <div class="panel-heading">                  <h3 class="panel-title">操作欄</h3>              </div>              <div class="panel-body">                  <div>                      <a href="{% url 'app01_publish_add' %}?next={{ url }}" class="">添加出版社</a>                  </div>                  <div>                      <a href="" class="">添加作者</a>                  </div>              </div>          </div>      </div>      <div class="container outer">          <div class="row">              <div class="col-md-6 col-md-offset-3">                  <div style="color: blue;font-size: 50px;text-align: center;font-family: 華文隸書" class="title">添加{{ zw_name }}</div>                  <form action="" method="post" novalidate>                      {% csrf_token %}                      {% for field in form %}                          <div class="form-group ">                              <label for="id_{{ field.name }}">{{ field.label }}</label>                              {{ field }}<span style="color: red">{{field.errors }}</span>                          </div>                      {% endfor %}                      <button class="submit btn btn-info pull-right">提交</button>                  </form>              </div>          </div>      </div>      <script>      $('input,select').addClass('form-control');      </script>  </body>  </html>

  其實從程式碼層面上講,做add校驗和標籤渲染沒多大區別,但在前端頁面上看是有區別的,forms組件只能渲染成type=text的input標籤,多於一對多和多對多的欄位就無能為力了,還得自己去寫。但ModelForm就不一樣了,它會把你的一對多的欄位渲染成單選的select標籤,把多對多的欄位渲染成多選的select標籤,這樣就相當方便了。如下圖:

  ModelForm可以渲染select標籤,它在編輯頁面還有更強大的功能,你只需把一個模型類的對象傳給他,他就可以幫你把對象每個值取出來,然後賦予標籤的value,在更新數據時也不用寫update了,用save()就搞定了。這就解決了我們要手動的去把要編輯的對象每個欄位的值取出來放入標籤中。

  views.py

def editbook(request,edit_book_id):      edit_book = models.Book.objects.filter(pk=edit_book_id).first()      if request.method=="POST":          form = BookModelForm(request.POST,instance=edit_book)          if form.is_valid():              form.save()       #在這裡用save()的前提是校驗的時候你把要編輯的對象傳給ModelForm的instance參數了,不然就相當於重新創建一條表記錄              return redirect("/books/")          else:              return render(request, 'editbook.html', locals())      else:          form=BookModelForm(instance=edit_book)     #要把編輯的對象傳給ModelForm的instance參數          return render(request,'editbook.html',locals())

  edit.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Title</title>      <link rel="stylesheet" href="/static/css/bootstrap.css">      <script src="/static/jquery-3.3.1.js"></script>      <script src="/static/js/bootstrap.js"></script>      <style>          .outer{              margin-top: 200px;          }          .title{              margin-bottom: 50px;          }          ul{              list-style: none;              margin: 0;              padding: 0;          }          .left{              position: fixed;              left: 0;              top: 20px;          }      </style>  </head>  <body>      <div class="left">          <div class="panel panel-primary">              <div class="panel-heading">                  <h3 class="panel-title">操作欄</h3>              </div>              <div class="panel-body">                  <div>                      <a href="{% url 'app01_publish_add' %}?next={{ url }}" class="">添加出版社</a>                  </div>                  <div>                      <a href="" class="">添加作者</a>                  </div>              </div>          </div>      </div>      <div class="container outer">          <div class="row">              <div class="col-md-6 col-md-offset-3">                  <div style="color: blue;font-size: 50px;text-align: center;font-family: 華文隸書" class="title">編輯{{ zw_name }}</div>                  <form action="" method="post" novalidate>                      {% csrf_token %}                      {% for field in form %}                          <div class="form-group ">                              <label for="id_{{ field.name }}">{{ field.label }}</label>                              {{ field }}<span style="color: red">{{field.errors }}</span>                          </div>                      {% endfor %}                      <button class="submit btn btn-info pull-right">提交</button>                  </form>              </div>          </div>      </div>      <script>      $('input,select').addClass('form-control');      </script>  </body>  </html>

  結果:

   總之啊,ModelForm是相當的好用,一直以來,我的叫法應該都有問題,我一直把forms組件下的Form叫成forms組件,之前寫的Form和今天寫的ModelForm都屬於forms組件,大家請原諒我。