前言:
為什么要用form去驗證呢?
我們提交的是form表單,在看前端源碼時如果檢查到POST URL及我們提交的字段,如果沒有驗證我們是否可以直接POST數據到URL,后台並沒有進行校驗,直接處理,那樣會不會對我們系統產生影響?答案是肯定的,FORM的作用就是起到一定的數據保護作用加一層校驗將不合法數據丟棄
1.針對上一章 django form表單驗證 補充
form表單補充,select框
#!/usr/bin/env python # -*- coding: utf-8 -*- import re from django import forms from django.core.exceptions import ValidationError def phone_validate(value): phone_re = re.compile(r'^(13[0-9]|15[012356789]|17[0678]|18[0-9]|14[57])[0-9]{8}$') if not phone_re.match(value): raise ValidationError('手機號碼格式錯誤') class TestForm(forms.Form): user_type_choice = ( (0, u'普通用戶'), (1, u'管理員'), ) user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice, attrs={'class': "form-control"}))
注:
widget 生成前端展示 .SELECT選擇下拉框,attrs屬性class前端顯示類
二. 通過model form來自定義表單
1. 字段類型
生成的表單類中將具有和指定的模型字段對應的表單字段,順序為fields 屬性中指定的順序。
Form field | |
---|---|
AutoField | Not represented in the form |
BigIntegerField | IntegerField with min_value set to -9223372036854775808 and max_value set to 9223372036854775807. |
BooleanField | BooleanField |
CharField | CharField with max_length set to the model field’s max_length |
CommaSeparatedIntegerField | CharField |
DateField | DateField |
DateTimeField | DateTimeField |
DecimalField | DecimalField |
EmailField | EmailField |
FileField | FileField |
FilePathField | FilePathField |
FloatField | FloatField |
ForeignKey | ModelChoiceField (see below) |
ImageField | ImageField |
IntegerField | IntegerField |
IPAddressField | IPAddressField |
GenericIPAddressField | GenericIPAddressField |
ManyToManyField | ModelMultipleChoiceField (see below) |
NullBooleanField | NullBooleanField |
PositiveIntegerField | IntegerField |
PositiveSmallIntegerField | IntegerField |
SlugField | SlugField |
SmallIntegerField | IntegerField |
TextField | CharField with widget=forms.Textarea |
TimeField | TimeField |
URLField | URLField |
完整示例:
1. 數據模型
創建:

1 from django.db import models 2 3 4 # Create your models here. 5 6 7 class Publisher(models.Model): 8 name = models.CharField(max_length=30, unique=True) 9 address = models.CharField(max_length=50) 10 city = models.CharField(max_length=60) 11 state_province = models.CharField(max_length=60) 12 country = models.CharField(max_length=60) 13 website = models.URLField() 14 15 def __str__(self): 16 return self.name 17 18 19 class Author(models.Model): 20 first_name = models.CharField(max_length=32) 21 last_name = models.CharField(max_length=32) 22 email = models.EmailField() 23 24 def __str__(self): 25 name = self.first_name + self.last_name 26 return name 27 28 29 class Book(models.Model): 30 name = models.CharField(max_length=128) 31 authors = models.ManyToManyField(Author) 32 publisher = models.ForeignKey(Publisher) 33 publish_date = models.DateField() 34 35 def __str__(self): 36 return self.authors

1 DATABASES = { 2 'default': { 3 'ENGINE': 'django.db.backends.mysql', 4 'NAME': os.environ.get("MYSQL_NAME", 'modelform'), 5 'USER': os.environ.get("MYSQL_USER", 'root'), 6 'PASSWORD': os.environ.get("MYSQL_PASSWD", 'password'), 7 'HOST': os.environ.get("MYSQL_HOST", '192.168.1.102'), 8 'PORT': os.environ.get("MYSQL_PORT", 3306), 9 } 10 }
生成:
python manage.py makemigrations
python manage.py migrate
只是舉例詳見參考
https://docs.djangoproject.com/en/1.11/topics/db/models/
http://www.cnblogs.com/jl-bai/p/5798860.html
注:
報錯:
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module: No module named 'MySQLdb'
原因:無mysql客戶端
解決方案:
在project 的root_dir 下__init__文件中寫入
import pymysql pymysql.install_as_MySQLdb()
(一)數據前端展示
2. 后台VIEWS
from django.views.generic.base import TemplateView from app01.forms import BookModelForm class Index(TemplateView): template_name = "index.html" def get(self, request, *args, **kwargs): form = BookModelForm() return self.render_to_response(context={"form": form})
3. ModelForm
#!/usr/bin/env python # -*- coding: utf-8 -*- from django import forms from app01.models import Book class BookModelForm(forms.ModelForm): class Meta: model = Book fields = ['name', 'authors', 'publish_date'] widgets = { 'name': forms.TextInput(attrs={'class': "form-control"}), 'authors': forms.Select(attrs={'class': "form-control"}), 'publish_date': forms.DateInput(attrs={'class': "form-control", 'placeholder': "YYYY-MM-DD"}) }
4. 前端展示
{% load staticfiles %} {% load i18n admin_static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>django model form</title> <link href="{% static 'css/bootstrap.css' %}" rel="stylesheet"/> </head> <body> <div style="width: 980px;margin: 0 auto"> <div class="col-lg-6"> <form action="{% url 'index' %}" method="POST">{% csrf_token %} {{ form }} <input class="btn btn-primary m-r-5 m-b-5" type="submit" value="提 交"> </form> </div> </div> </body> </html>
訪問:
(二) 模型表單的驗證
1. 驗證模型
與普通的表單驗證類型類似,模型表單的驗證在調用is_valid() 或訪問errors 屬性時隱式調用,或者通過full_clean() 顯式調用,盡管在實際應用中你將很少使用后一種方法。
模型的驗證(Model.full_clean())在表單驗證這一步的內部觸發,緊跟在表單的clean() 方法調用之后。
警告
Clean 過程會以各種方式修改傳遞給模型表單構造函數的模型實例。例如,模型的日期字段將轉換成日期對象。驗證失敗可能導致模型實例處於不一致的狀態,所以不建議重新使用它。
1.1 重寫clean() 方法
可以重寫模型表單的clean() 來提供額外的驗證,方法和普通的表單一樣。
模型表單實例包含一個instance 屬性,表示與它綁定的模型實例。
警告
ModelForm.clean() 方法設置一個標識符, 使得模型驗證 這一步驗證標記為unique、 unique_together 或unique_for_date|month|year 的模型字段的唯一性。
如果你需要覆蓋clean() 方法並維持這個驗證行為,你必須調用父類的clean() 方法。
與django form表單驗證 中定義clean方法相同
模型error_messages 的注意事項
表單字段級別或表單級別的錯誤信息永遠比模型字段級別的錯誤信息優先。
模型字段的錯誤信息只用於模型驗證步驟引發ValidationError 的時候,且不會有對應的表單級別的錯誤信息。
2. save()方法
每個ModelForm還具有一個save() 方法。這個方法根據表單綁定的數據創建並保存數據庫對象。模型表單的子類可以用關鍵字參數instance 接收一個已經存在的模型實例;如果提供,save() 將更新這個實例。如果沒有提供,save() 將創建模型的一個新實例:

1 class Index(TemplateView): 2 template_name = "index.html" 3 4 def get(self, request, *args, **kwargs): 5 form = BookModelForm() 6 return self.render_to_response(context={"form": form}) 7 8 def post(self,request): 9 form = BookModelForm() 10 if form.is_valid(): 11 form.save() 12 return redirect("home") 13 return self.render_to_response(context={"form": form})
如果表單沒有驗證,save() 調用將通過檢查form.errors 來進行驗證。如果表單中的數據不合法,將引發ValueError —— 例如,如果form.errors 為True。
3. 先擇用到的字段
class BookModelForm(forms.ModelForm): class Meta: model = Book fields = ['name', 'authors', 'publish_date'] widgets = { 'name': forms.TextInput(attrs={'class': "form-control"}), 'authors': forms.Select(attrs={'class': "form-control"}), 'publish_date': forms.DateInput(attrs={'class': "form-control", 'placeholder': "YYYY-MM-DD"}) }
在ModelForm中我們通常使用fields 屬性顯式設置所有將要在表單中編輯的字段,如果不這樣做,當表單不小心允許用戶設置某些特定的字段,特別是有的字段添加到模型中的時候,將很容易導致安全問題。
另外一種方式是自動包含所有的字段,或者排除某些字段。這種基本方式的安全性要差很多,而且已經導致大型的網站受到嚴重的利用。
3.1 設置fields 屬性為特殊的值'__all__' 以表示需要使用模型的所有字段。例如:
class BookModelForm(forms.ModelForm): class Meta: model = Book fields = '__all__'
3.2 設置ModelForm 內聯的Meta 類的exclude 屬性為一個要從表單中排除的字段的列表
class BookModelForm(forms.ModelForm): class Meta: model = Book exclude = ()
exclude = () 與 fields = '__all__' 達到效果相同選取所有字段
如果 exclude=['name'],那么排除“name"字段其他字段都會顯示
3.3 多個外鍵對同一個模型
如果一個模型在同一個模型中包含多個外鍵,則需要使用fk_name手動解決歧義。例如,考慮以下模型:
class Friendship(models.Model): from_friend = models.ForeignKey(Friend, related_name='from_friends') to_friend = models.ForeignKey(Friend, related_name='friends') length_in_months = models.IntegerField()
要解決此問題,您可以使用fk_name到inlineformset_factory()
FriendshipFormSet = inlineformset_factory(Friend, Friendship, fk_name='from_friend', ... fields=('to_friend', 'length_in_months'))
具體參考:
https://docs.djangoproject.com/en/1.11/topics/forms/modelforms/