在前面有篇博客,我寫了一個叫forms組件的東西,可以幫助我們完成校驗數據、渲染標簽功能和在前端頁面局部刷新功能,功能封裝的已經很好了,當時已經很開心了。但萬萬沒想到,還有比它功能更強大的東西。forms組件只能渲染出type=text類型的標簽,而且還要我們寫無數多個欄位,然後跟上校驗條件,用fo ...
在前面有篇博客,我寫了一個叫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組件,大家請原諒我。