Django框架(十)--ORM多對多關聯關係三種建立方式、form組件

多對多的三種建立方式html

1.全自動(就是日常咱們建立表多對多關係的方式)前端

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
                authors = models.ManyToManyField(to='Author')

class Author(models.Model):
    name = models.CharField(max_length=32)
優點:不須要你手動建立第三張表
不足:因爲第三張表不是你手動建立的,也就意味着第三張表字段是固定的沒法作擴展

2.純手動(手動建關係表)git

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)

class Author(models.Model):
    name = models.CharField(max_length=32)
                
                
class Book2Author(models.Model):
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')
    create_time = models.DateField(auto_now_add=True)
優點:第三張能夠任意的擴展字段
不足:orm查詢不方便

3.半自動(推薦使用)django

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
    # through 告訴django orm 書籍表和做者表的多對多關係是經過Book2Author來記錄的
    # through_fields 告訴django orm記錄關係時用過Book2Author表中的book字段和author字段來記錄的
    """
    多對多字段的
    add
    set
    remove
    clear不支持
    """
                             
class Author(models.Model):
    name = models.CharField(max_length=32)
    # books = models.ManyToManyField(to='Book', through='Book2Author', through_fields=('author', 'book'))

#手動建立的第三張表
class Book2Author(models.Model):
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')
    create_time = models.DateField(auto_now_add=True)
優勢:即不用本身建立第三張表了,也能夠任意擴展字段了
缺點:這種多對多關係中不能使用add/set/remove/clear這些方法了

form組件後端

1、什麼是form組件,能夠幹什麼函數

1.forms組件就是一個類,能夠檢測前端傳來的數據,是否合法。
例如,前端傳來的郵箱數據,判斷郵件格式對不對,用戶名中不能以什麼開頭,等等  >>>校驗數據
2.還能夠前端頁面搭建 >>> 渲染頁面
3.展現錯誤信息 >>> 渲染錯誤信息

2、form組件的使用post

1.使用語法測試

from django.shortcuts import render, HttpResponse
from django import forms

#1.先寫一個類,繼承Form
class MyForm(forms.Form):
    #定義屬性,用來校驗
    #限制最大長度爲8,最小長度爲3
    name = forms.CharField(max_length=8,min_length=3)
    pwd = forms.CharField(max_length=8,min_length=3)
    #校驗郵箱
    email = forms.EmailField()


#2.在視圖函數中使用MyForm來校驗數據
#實例化產生對象,將須要校驗的數據傳入(能夠傳字典,能夠不傳),這個數據是前端傳遞過來的數據
myform = MyForm(request.POST)

#3.校驗,is_valid若是是true表示校驗成功(知足myform裏面的全部條件),反之,驗證失敗
if myform.is_valid():
    print(myform.cleaned_data)  #打印校驗經過的數據
    return HttpResponse('校驗成功')
    
else:
    #校驗失敗的信息  是一個字典,它是全部錯誤信息 {錯誤字段名:[錯誤緣由]}
    print(myform.errors)  #errors是一個列表,表示每一個字段的錯誤信息
    return HttpResponse('校驗失敗')

方法總結:ui

clean_data  驗證經過的數據
errors  錯誤數據的對象
errors.as_data  錯誤數據的信息

注意事項spa

1.自定義的類中全部的字段默認都是必需要傳值的
2.能夠額外傳入類中沒有定義的字段名,forms組件不會去校驗,也就是多傳沒有關係
3.實例化時,傳入必須是字典,也能夠不傳
form_obj = views.LoginForm({'username':'jason','password':'123456','email':'123@qq.com'})
form_obj.is_valid()
True
                    
form_obj = views.LoginForm({'username':'jason','password':'123456'})
form_obj.is_valid()
False
                    
form_obj = views.LoginForm({'username':'jason','password':'123456','email':'123@qq.com','hobby':'read'})
form_obj.is_valid()
True

2.組件的參數及其餘操做方式

max_length   #表明該字段最長能夠爲多少
min_length    #表明該字段最短能夠爲多少
error_messages    #設置錯誤信息的屬性
required      #默認值爲True,意思是你前端傳來的字段必須有它,沒有的話校驗失敗
label    #註釋信息label ='用戶名'   在前端渲染能夠直接對象.label獲取值
initial   #設置默認值

widget #控制標籤屬性和樣式 widget = widgets.PasswordInput() #你在模板渲染的時候,就會渲染成Input框,password樣式 控制標籤屬性 widget = widgets.PasswordInput(attrs={'class':'form-control c1 c2','username':'jason'})
#例子 pwd = forms.CharField(max_length=8,min_length=3,required=True,label='密碼'error_messages = {'max_length':'最長是8','min_length':'最短是3','required':'這個必須填'} )
其餘操做方式
#
單選的radio框 gender = forms.ChoiceField( choices=((1, ""), (2, ""), (3, "保密")), label="性別", initial=3, widget=forms.widgets.RadioSelect() ) # 單選select hobby = forms.ChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),), label="愛好", initial=3, widget=forms.widgets.Select() ) # 多選的select框 hobby1 = forms.MultipleChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),), label="愛好", initial=[1, 3], widget=forms.widgets.SelectMultiple() ) # 單選的checkbox keep = forms.ChoiceField( label="是否記住密碼", initial="checked", widget=forms.widgets.CheckboxInput() ) # 多選的checkbox hobby2 = forms.MultipleChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),), label="愛好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )

3、渲染頁面、渲染錯誤信息

#form組件能夠在視圖函數中使用,也能夠在模板中使用
#視圖層
def reg(request):
    myform = MyForm()
   if request.method == 'POST':
   myform = MyForm(request.POST)
     print(request.POST) #<QueryDict: {'name': ['小張'], 'pwd': ['123123'], 'email': ['119@qq.com']}>
return render(request,'reg.html',locals()) #把myform對象傳到前端頁面了
#模板層
#第一種渲染方式(封裝程度過高,通常只用於本地測試,一般不適用)
<form action='' method='post'>
         {{ myform.as_p }}
         {{ myform.as_ul }}
         {{ myform.as_table}}
        <input type='submit' value = '提交'></input>
 </form>

#第二種渲染頁面的方式(可擴展性高,書寫麻煩)
<form action='' method='post'>
        {{myform.name.label}}:{{myform:name}} 
        {{myform.pwd.label}}:{{myform:pwd}} 
        {{myform.email.label}}:{{myform:email}} 
        <input type='submit' value = '提交'></input>
 </form>

#第三種渲染方式(推薦)
<form action='' method='post'>
        {% for foo in myform %}
           <p> {{ foo.lable }} : {{ foo }} 
           <span>{{ foo.errors.0 }}</span>   //注意後面加0  錯誤信息
           </p>
        <input type='submit' value = '提交'></input>
</form>      

注意事項:

1.forms組件在幫你渲染頁面的時候 只會渲染獲取用戶輸入的標籤  提交按鈕須要你手動添加               
2.input框的label註釋  不指定的狀況下 默認用的類中字段的首字母大寫

 4、鉤子函數(HOOK)

forms組件暴露給用戶,能夠自定義的校驗規則    用法:在自定義的form類中定義一個函數便可

1.局部鉤子(針對某一個字段作額外的校驗) 

名字叫:clean_字段名,內部,取出該字段進行校驗,若是經過,將該字段返回,若是失敗,拋異常

#函數名:clean_字段名字
def clean_name(self):   #self是當前form對象
    name = self.cleaned_data.get('name')    #從驗證經過的數據中再次篩選
    if '666' in username:
        #失敗,拋異常,把異常信息以('key','value')形式寫入add_error中
        self.add_error('name','不能包含666')   
    #正常,把name返回到clean_data,將name寫入clean_data字典中
    return name

注意點:

  • 這些函數是寫在自定義類form裏面的,須要校驗哪一個字段就以 clean_字段名寫一個校驗函數
  • 校驗失敗,經過add_error('字段名','錯誤信息'),以{key:value}形式寫入到errors字典中
  • 校驗成功,返回name到clean_data,寫入clean_data字典中

2.全局鉤子(針對多個字段作額外的校驗)

#重寫clean方法
def clean(self):
    #能走到這步,說明前面的校驗已經經過了,下面校驗兩次兩次密碼是否輸入一致
    pwd = self.clean_data.get('pwd')
    re_pwd = self.clean_data.get('re_pwd')
    if pwd == re_pwd:
        #校驗成功,直接返回clean_data
        return self.clean_data
    else:
        #校驗失敗,把錯誤信息放入errors中
        self.add_error('re_pwd','兩次密碼輸入不一致')
        或者
        #拋出異常
        raise ValidationError('兩次密碼不一致')

注意點:

  • 校驗失敗,有兩種寫法,拋異常,將異常信息以{'__all__':[value,]}寫入errors字典中
  • 校驗成功,返回clean_data字典
  • 拋出異常類型爲 ValidationError, from django.core.exceptions import ValidationError導入

鉤子錯誤信息渲染注意點:

  • 局部鉤子拋出的異常會添加到該字段中的錯誤信息中,獲取錯誤信息

   前端: for循環對象  {{ foo.errors.0}}

  • 全局鉤子拋出的異常會添加到__all__中,獲取錯誤信息:

  後端: myform.errors.get('__all__')[0]   注意先判斷myform.errors.get('__all__')是否存在

     前端:{{ myform.errors.__all__.0}}

  • 能走到這些函數,說明前面的form組件的那些校驗都成功了,此時就能夠從clean_data中取數據,由於此時clean_data中的數據所有符合,都找的到,並且clean_data是字典比較好取值。

 5、完整的form組件校驗

 1.視圖層

from django.shortcuts import render, HttpResponse, redirect

# forms組件數據校驗的功能
# 第一步:先要繼承Form
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError


# 寫一個類
class MyForm(forms.Form):
    # 定義一個屬性,能夠用來校驗字符串類型
    # 限制最大長度是8,最小長度是3
    name = forms.CharField(max_length=8, min_length=3, label='用戶名'
                           error_messages={'max_length': '最長是8', 'min_length': '最短是3', 'required': '這個必須填'},
                           widget=widgets.TextInput(attrs={'class': 'form-control'}))
    
    pwd = forms.CharField(max_length=8, min_length=3, required=True, label='密碼',
                          error_messages={'max_length': '最長是8', 'min_length': '最短是3', 'required': '這個必須填'},
                          widget=widgets.PasswordInput())
    
    re_pwd = forms.CharField(max_length=8, min_length=3, required=True, label='確認密碼',
                             error_messages={'max_length': '最長是8', 'min_length': '最短是3', 'required': '這個必須填'},
                             widget=widgets.PasswordInput())
    # 校驗是不是郵箱格式
    email = forms.EmailField(label='郵箱', error_messages={'required': '這個必須填', 'invalid': '不符合郵箱格式'})

    # aa = forms.CharField(label='選擇', error_messages={'required': '這個必須填', 'invalid': '不符合郵箱格式'},widget=widgets.CheckboxInput())
    def clean_name(self):
        # self:當前form對象
        name = self.cleaned_data.get('name')
        if name.startswith('sb'):
            # 失敗,拋異常
            raise ValidationError('不能以傻逼開頭')
        # 正常,把name返回
        return name

    def clean(self):
        pwd = self.cleaned_data.get('pwd')
        re_pwd = self.cleaned_data.get('re_pwd')
        if pwd == re_pwd:
            return self.cleaned_data
        else:
            raise ValidationError('兩次密碼不一致')


def index_form(request):
    # 生成對象時(實例化),須要傳入要校驗的數據(字典)
    if request.method == 'GET':
        myform = MyForm()
        return render(request,'indxe2.html',locals())
    elif request.method == 'POST':
        myform = MyForm(request.POST)
        if myform.is_valid():
            # print(myform.cleaned_data)   # 驗證經過的數據
            # models.User.objects.create(name='lqz',pwd='123',re_pwd='123)
            myform.cleaned_data.pop('re_pwd')
            models.User.objects.create(**myform.cleaned_data)
            return redirect('http://www.baidu.com')
        else:
            all_error = myform.errors.get('__all__')
            if all_error:
                all_error = all_error[0]
             # print(myform.errors.as_data)

    return render(request, 'indxe3.html', locals())

2.模板層

<form action="" method="post" novalidate>
    {% for foo in myform %}
        <p>{{ foo.label }}:{{ foo }} <span>{{ foo.errors.0 }}</span></p>
    {% endfor %}

    <input type="submit" value="提交"><span>{{ all_error }}</span>
</form>
相關文章
相關標籤/搜索