目錄css
class Book(models.Model): title = models.CharField(max_length=32) # # 經過ORM自帶的ManyToManyField自動建立第三張表 authors=models.ManyToManyField(to='Author') class Author(models.Model): name = models.CharField(max_length=32)
優勢:操做簡便,第三張表都是orm自動建立html
內置的操做第三張表的方法:add、remove、set、clear前端
缺點:自動建立的第三張表沒法擴展、修改字段,表的擴展性較差python
class Book(models.Model): title = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) # 本身建立第三張表,分別經過外鍵關聯書和做者 class BookAuthor(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to = 'Author') create_time = models.DataField(auto_now_add = True)
優勢:第三張表的字段個數和類型均可以本身定義,靈活性更高;jquery
缺點:再也不支持orm跨表查詢,就沒有了正反向的概念;git
當ManyToManyField只有一個參數to的狀況下, orm會自動幫你建立第三張表;
若是還有額外的through和through_fields參數,第三張表就要本身建立, orm還會維護與第三張表的關係維,可以繼續使用orm的跨表查詢;
through 指定第三張關係表;
through_fields 指定第三張關係表中的兩個外鍵字段。ajax
class Book(models.Model): title = models.CharField(max_length=32) author = models.ManyToManyField(to='Author',,through='BookAuthor',through_fields=('book','author')) class Author(models.Model): name = models.CharField(max_length=32) book = models.ManyToManyField(to='Book',,through='BookAuthor',through_fields=('author','book')) # through_fields接受一個元組('field1','field2'):其中field1是定義ManyToManyField的模型外鍵的名(author),field2是關聯目標模型(book)的外鍵名。 class BookAuthor(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to = 'Author') create_time = models.DataField(auto_now_add = True)
優勢:能夠任意的添加和修改第三張表中的字段,而且支持orm跨表查詢正則表達式
缺點:再也不支持add、remove、clear、set方法的使用數據庫
form表單的主要功能:django
生成頁面可用的HTML標籤;對用戶提交的數據進行校驗;保留上次的輸入內容;
需求:在瀏覽器頁面,經過form表單中的input框獲取用戶輸入,傳到後端進行校驗用戶的輸入是否合法;而後在對應的input框後展現一些提示信息;
這裏利用標籤的渲染(其實用ajax和BOM操做也能實現)
def register(request): back_dic = {'username':'','password':''} if request.method == 'POST': username = request.POST.get('usename') password = request.POST.get('password') if '小朋友' in username: back_dic['usernmae'] = '敏感信息!不符合!' if len(password) < 4: back_dic['password'] = '你過短!不行呀!' return render(request,'register.html',local())
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link rel="stylesheet" href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> </body> </html>
以上須要本身手寫HTML代碼獲取用戶輸入,傳到後端,若是數據有誤,還需展現錯誤信息,比較麻煩,下面利用django自帶的form組件來實現。
使用form組件首先要定義一個類,繼承django的Form類
# views.py from django import forms class MyForm(forms.Form): # username字段,最少3位,最多8位 username = forms.CharField(max_length=8,min_length=3) # password字段,最少3位,最多8位 password = forms.CharField(max_length=8,min_length=3) # email字段,必須是郵箱格式 email = forms.EmailField()
# 在Django Console中校驗 自動搭建測試環境 In[2]:from app01 import views In[3]:form_obj = views.MyForm({'username':'zhang','password':'123','email':'110117@qq.com'}) In[4]:form_obj.is_valid Out[4]: <bound method BaseForm.is_valid of <MyForm bound=True, valid=Unknown, fields=(username;password;email)>> In[5]:form_obj.is_valid() Out[5]: True # 只有當你傳入的所有數據都符合校驗規則的狀況下才爲True,不然爲False # 查看不符合規則的字段和錯誤理由 In[6]:form_obj.errors Out[6]: {} # 符合規則的數據,不符合的會被排除掉 In[7]:form_obj.cleaned_data Out[7]: {'username': 'zhang', 'password': '123', 'email': '110117@qq.com'} # 若是出入的數據不符合定義字段指定的長度,錯誤的字段會放在form_obj.errors中;好比: In[8]:form_obj.errors Out[8]:{'password': ['Ensure this value has at least 3 characters (it has 2).'],'email': ['Enter a valid email address.'] } # 傳數據的時候,多傳不報錯 In[9]:form_obj = views.MyForm({'username':'zhang','password':'123','email':'110117@qq.com','gender':'male'}) In[10]form_obj.is_valid() Out[10]: True # 少傳對應的字段就不行 In[11]:form_obj = views.MyForm({'username':'zhang','password':'123'}) In[12]:form_obj.is_valid() Out[13]: False
forms組件只會幫你渲染獲取用戶輸入的標籤 不會幫你渲染提交按鈕 須要你本身手動添加
字段類括號內的label能夠修改input框前面的註釋(提示信息)
from django import forms class MyForm(forms.Form): username = forms.CharField(max_length=8,min_length=3,label='用戶名') # label 修改用戶輸入框前面的提示信息 password = forms.CharField(max_length=8,min_length=3,label='密碼') email = forms.EmailField(label='郵箱') def index(request): form_obj = MyForm(request.POST) return render(request, 'index.html', locals())
forms組件渲染標籤方式1
封裝程度態高 不推薦使用 可是能夠用在本地測試
<p>forms組件渲染標籤方式1</p> {{ form_obj.as_p }} <!--自動渲染全部input框 --> {{ form_obj.as_ul }} {{ form_obj.as_table }}
forms組件渲染標籤方式2
不推薦使用 寫起來太煩了
<p>forms組件渲染標籤方式2</p> <!--獲取input框前面的Username 獲取的是一個input輸入框--> {{ form_obj.username.label }}{{ form_obj.username }} {{ form_obj.username.label }}{{ form_obj.password }} {{ form_obj.username.label }}{{ form_obj.email }}
forms組件渲染標籤方式3
推薦使用
<p>forms組件渲染標籤方式3:推薦使用 </p> {% for form in form_obj %} <p>{{ form.label }}{{ form }}</p> <!--form 等價於你方式2中的對象點字段名,是一個input輸入框--> {% endfor %}
from django import forms class MyForm(forms.Form): username = forms.CharField(max_length=8,min_length=3,label='用戶名', error_messages={ 'max_length':'用戶名不能超過八位', 'min_length':'用戶名不能少於三位', 'required':'用戶名不能爲空', } ) # label 修改用戶輸入框前面的提示信息 password = forms.CharField(max_length=8,min_length=3,label='密碼', error_messages={ 'max_length': '密碼不能超過八位', 'min_length':'密碼不能少於三位', 'required': '密碼不能爲空', } ) email = forms.EmailField(label='郵箱', error_messages={ 'required':'郵箱不能爲空!', 'invalid':'郵箱格式錯誤', }) def index(request): form_obj = MyForm() print(request.POST) # <QueryDict: {'username': ['zhang'], 'password': ['123456'], 'email': ['1101172195@qq.com']}> if request.method=='POST': form_obj = MyForm(request.POST) # MyForm類須要傳入一個字典,而request.POST獲取的用戶的輸入偏偏也是字典 if form_obj.is_valid(): # 跟上面的變量名要一致,由於兩次不衝突,都要拿到html文件中給同一份代碼渲染 print(form_obj.cleaned_data) return HttpResponse('輸入真確!') else: print(form_obj.errors) return render(request, 'index.html', locals())
novalidate 在form標籤內寫上它,告訴前端不讓其對用戶輸入的數據作校驗
forms.errors 獲取後端校驗後錯誤的報錯信息,傳給前端;
forms.errors.0 改變報錯信息在input輸入框後面展現
forms.label input框前面的提示信息
forms 獲取一個input輸入框
{#novalidate讓前端不對輸入信息是否合法作校驗#} <form action="" method="post" novalidate> <p>form組件的渲染方式3:</p> <!--推薦使用--> {% for forms in form_obj %} <p> {# 獲取提示信息和input輸入框#} {{ forms.label }}{{ forms }} {# 這裏forms.errors也能拿到後端的報錯信息(在input框下面),渲染到瀏覽器,forms.errors.0把報錯信息的展現到input框後面#} <span>{{ forms.errors.0 }}</span> </p> {% endfor %} <input type="submit"> </form>
字段類括號內能夠連續寫多個參數
from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '請輸入數字'), RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')], )
當你以爲上面的全部的校驗還不可以知足你的需求 ,你能夠考慮使用鉤子函數,是一個函數 函數體內你能夠寫任意的校驗代碼。
鉤子函數執行條件:在類內部定義的字段括號設定的條件都知足以後纔會走到鉤子函數;若是前面的條件都不知足,不會走到鉤子函數。
用法:直接在類內部定義django給咱們定義好的函數,根據須要本身選擇。包括:
clean、clean_confirm_password、clean_email、clean_password、clean_username
局部鉤子:校驗單個字段
全局鉤子:校驗多個字段
# 使用forms組件的第一步 必須先寫一個類 from django import forms class MyForm(forms.Form): # username字段 最少三位 最多八位 username = forms.CharField(max_length=8,min_length=3,label='用戶名',initial='默認值', error_messages={ 'max_length':'用戶名最長八位', 'min_length':'用戶名最短三位', 'required':'用戶名不能爲空' },required=False, widget=forms.widgets.TextInput({'class':'form-control c1 c2','username':'jason'}) ) # password字段 最少三位 最多八位 password = forms.CharField(max_length=8,min_length=3,label='密碼', error_messages={ 'max_length': '密碼最長八位', 'min_length': '密碼最短三位', 'required': '密碼不能爲空' },widget=forms.widgets.PasswordInput() ) confirm_password = forms.CharField(max_length=8, min_length=3, label='確認密碼', error_messages={ 'max_length': '確認密碼最長八位', 'min_length': '確認密碼最短三位', 'required': '確認密碼不能爲空' }, ) # email字段 必須是郵箱格式 email = forms.EmailField(label='郵箱',error_messages={ 'required':'郵箱不能爲空', 'invalid':'郵箱格式錯誤' }) # 校驗用戶名中不能含有666 局部鉤子 def clean_username(self): username = self.cleaned_data.get('username') if '666' in username: # 給username所對應的框展現錯誤信息 # self.add_error('username','光喊666是不行的') raise ValidationError('到底對不對啊') # 將username數據返回 return username # 校驗密碼 確認密碼是否一致 全局鉤子 def clean(self): password = self.cleaned_data.get("password") confirm_password = self.cleaned_data.get("confirm_password") if not password == confirm_password: self.add_error('confirm_password','兩次密碼不一致') # 將全局的數據返回 return self.cleaned_data
其餘字段及參數
label input對應的提示信息 initial input框默認值 required 默認爲True控制字段是否必填 widget 給input框設置樣式及屬性,也能設置input輸入框的類型(type) widget=forms.widgets.TextInput({'class':'form-control c1 c2','username':'li'}) # 增長屬性 widget=forms.widgets.TextInput(attrs={'class':'form-control c1c2','username':'li'}) widget=forms.widgets.TextInput() # 默認普通文本類型 widget=forms.widgets.PasswordInput() # 密文密碼
radioSelect
單radio值爲字符串
class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用戶名", initial="張三", error_messages={ "required": "不能爲空", "invalid": "格式錯誤", "min_length": "用戶名最短8位" } ) pwd = forms.CharField(min_length=6, label="密碼") gender = forms.fields.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性別", initial=3, widget=forms.widgets.RadioSelect() )
單選Select
class LoginForm(forms.Form): ... hobby = forms.ChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"), ), label="愛好", initial=3, widget=forms.widgets.Select() )
多選Select
class LoginForm(forms.Form): ... hobby = forms.MultipleChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"), ), label="愛好", initial=[1, 3], widget=forms.widgets.SelectMultiple() )
單選checkbox
class LoginForm(forms.Form): ... keep = forms.ChoiceField( label="是否記住密碼", initial="checked", widget=forms.widgets.CheckboxInput() )
多選checkbox
class LoginForm(forms.Form): ... hobby = forms.MultipleChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),), label="愛好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )
choice字段注意事項
在使用選擇標籤時,須要注意choices的選項能夠配置從數據庫中獲取,可是因爲是靜態字段 獲取的值沒法實時更新,須要重寫構造方法從而實現choice實時更新。
方式一:
from django.forms import Form from django.forms import widgets from django.forms import fields class MyForm(Form): user = fields.ChoiceField( # choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.Select ) def __init__(self, *args, **kwargs): super(MyForm,self).__init__(*args, **kwargs) # self.fields['user'].choices = ((1, '上海'), (2, '北京'),) # 或 self.fields['user'].choices=models.Classes.objects.all().values_list('id','caption')
方式二:
from django import forms from django.forms import fields from django.forms import models as form_model class FInfo(forms.Form): authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # 多選 # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all()) # 單選
Field required=True, 默認爲空,True不能爲空,False容許爲空 widget=None, HTML插件 label=None, 用於修改input框前面的提示信息 initial=None, input框初始值 help_text='', 幫助信息(在標籤旁邊顯示) error_messages=None, 錯誤信息 {'required': '不能爲空', 'invalid': '格式錯誤'} validators=[], 自定義驗證規則 localize=False, 是否支持本地化 disabled=False, 是否能夠編輯 label_suffix=None Label內容後綴 CharField(Field) max_length=None, 最大長度 min_length=None, 最小長度 strip=True 是否移除用戶輸入空白 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 FloatField(IntegerField) ... DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 總長度 decimal_places=None, 小數位長度 BaseTemporalField(Field) input_formats=None 時間格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 時間間隔:%d %H:%M:%S.%f ... RegexField(CharField) regex, 自定製正則表達式 max_length=None, 最大長度 min_length=None, 最小長度 error_message=None, 忽略,錯誤信息使用 error_messages={'invalid': '...'} EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否容許空文件 ImageField(FileField) ... 注:須要PIL模塊,pip3 install Pillow 以上兩個字典使用時,須要注意兩點: - form表單中 enctype="multipart/form-data" - view函數中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 選項,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默認select插件 label=None, Label內容 initial=None, 初始值 help_text='', 幫助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查詢數據庫中的數據 empty_label="---------", # 默認空顯示內容 to_field_name=None, # HTML中value的值對應的字段 limit_choices_to=None # ModelForm中對queryset二次篩選 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 對選中的值進行一次轉換 empty_value= '' 空值的默認值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 對選中的每個值進行一次轉換 empty_value= '' 空值的默認值 ComboField(Field) fields=() 使用多個驗證,以下:即驗證最大長度20,又驗證郵箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象類,子類中能夠實現聚合多個字典去匹配一個值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件選項,目錄下文件顯示在頁面中 path, 文件夾路徑 match=None, 正則匹配 recursive=False, 遞歸下面的文件夾 allow_files=True, 容許文件 allow_folders=False, 容許文件夾 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,若是是::ffff:192.0.2.1時候,可解析爲192.0.2.1, PS:protocol必須爲both才能啓用 SlugField(CharField) 數字,字母,下劃線,減號(連字符) ... UUIDField(CharField) uuid類型 Django Form內置字段