1. 程式人生 > >Model、Form、ModelForm的比較

Model、Form、ModelForm的比較

關系 Nid edi 分享 報錯 數據 而在 textarea ssa

本節內容:

1:Model

2:Form

3:Model Form

1 2 3 http://www.cnblogs.com/wupeiqi/articles/6144178.html 武sir:Form組件 http://www.cnblogs.com/wupeiqi/articles/6216618.html 武sir:Model http://www.cnblogs.com/wupeiqi/articles/6229414.html 武sir:ModelForm

Model ==> 強大的數據庫操作,弱小的數據驗證。

Form ==>強大的數據驗證

ModelForm ===>二者結合,強大的數據驗證,適中的數據庫操作。在ModelForm是能夠封裝一個model對象。

1:Model

對於Model來說,他的驗證是需要自己去創建一個model對象,然後去進行判斷

model:
    針對單一字段 :full_clean 
    針對多個的字段: clean
    
    full_clean -- >字段正則判定 -- >clean方法(鉤子)
    他是沒有最終產物
    

views:

def fm(request):
    obj 
= models.News(title=root) ##full_clean就進行了驗證,如果要是有errors的話,就直接報錯,所以在進行驗證的時候,我們要自己做try判斷 obj.full_clean() ##進行model的驗證。裏面的def clean 方法 obj.save() ##報錯 django.core.exceptions.ValidationError: {__all__: [title不能是root]} return render(request,"form.html",locals())

models:

from django.db import models
from  django.core.exceptions import ValidationError


class News(models.Model):
    title = models.CharField(max_length=32)

    ##驗證錯誤會輸出到errors中去
    def clean(self):
        if self.title == "root":
            raise ValidationError("title不能是root")

model的源碼分析:

技術分享圖片
 def full_clean(self, exclude=None, validate_unique=True):
        """
        Call clean_fields(), clean(), and validate_unique() on the model.
        Raise a ValidationError for any errors that occur.
        """
        errors = {}
        if exclude is None:
            exclude = []
        else:
            exclude = list(exclude)

        try:
            self.clean_fields(exclude=exclude)  ####執行單個字段的驗證
        except ValidationError as e:
            errors = e.update_error_dict(errors)

        # Form.clean() is run even if other validation fails, so do the
        # same with Model.clean() for consistency.
        try:
            self.clean()   ####執行clean的方法驗證 
        except ValidationError as e:
            errors = e.update_error_dict(errors)  ###如果錯誤,把錯誤添加到errors中

        # Run unique checks, but only for fields that passed validation.
        if validate_unique:
            for name in errors:
                if name != NON_FIELD_ERRORS and name not in exclude:
                    exclude.append(name)
            try:
                self.validate_unique(exclude=exclude)
            except ValidationError as e:
                errors = e.update_error_dict(errors)

        if errors:   ###如果有錯誤,就直接報錯了,so 我們要自己去views視圖中去判斷,try
            raise ValidationError(errors)
View Code

2:Form

有著強大的驗證功能: 具體看源碼。

對於Form來說,是當一個請求來了,直接進行post數據的驗證,如果是錯誤會有一個obj.errors的錯誤對應。

Form :有強大的驗證
    針對單一字段的: full_clean
    自定義針對單一字段的: clean_username 
    針對多個字段的: clean 【or】port_clean (這個是不能出現異常,只能添加異常)

    is_valid() -- >full_clean () -->
                                -->每個字段的正則,每個字段clean_字段名()
                                -->clean_form -->clean(鉤子)
                                -->_post_clean(鉤子)
                                
    驗證後的產物:clean_date 或obj.errors        

Form中要獲取數據庫的實時數據:

兩種方式:

?
1 第二種方式,雖然是可以用但是它的可定制性差。是需要依賴model的

技術分享圖片

瀏覽器中:顯示

技術分享圖片

3:ModelForm

Model、Form、ModelForm三者的結合:

在公司比較大的時候:比如說:
    models文件是放在A項目中
    forms是放在B項目中 

froms是沒辦法導入models中的數據的。
我們就讓Form單獨的做數據驗證,而model就單純的做數據庫操作各司其職是完美的。 

但是dajngo還存在了一種叫ModelForm的東西,他是結合了model和Form的功能。

Form和ModelForm的繼承關系:

Form:
    繼承關系:
        UserForm -- > Form -- >BaseForm
ModelForm: 
    繼承關系:
        NewsModelForm -->ModelForm -->BaseModelForm -->BaseForm 
        
        
所以modelForm和Form是有同一個祖宗的,Form中的BaseForm的功能,ModelForm也一樣可以使用。 

ModelForm的簡單使用:

技術分享圖片
from django.db import models
from  django.core.exceptions import ValidationError

class User_Type(models.Model):
    name = models.CharField(max_length=32)
    def __str__(self):
        return self.name

class Tags(models.Model):
    name = models.CharField(max_length=32)
    def __str__(self):
        return self.name

class News(models.Model):
    title = models.CharField(max_length=32)
    type = models.ForeignKey(User_Type,on_delete=models.CASCADE,blank=True,null=True)
    tag = models.ManyToManyField(Tags)


    ##驗證錯誤會輸出到errors中去
    def clean(self):
        if self.title == "root":
            raise ValidationError("title不能是root")
model
from django.forms import  Form  ##Form要繼承的
from django.forms import ModelForm  ##ModelForm繼承
from  web import models

class NewsModelForm(ModelForm):
    class Meta:
        model = models.News  ##裏面必須要有一個model,因為他是對每個models類做增刪改查的
        fields =  "__all__"  ##指定了類,要需要指定要處理哪幾個字段 __all__是全部字段


def mf(request):
    if request.method == "GET":
        obj = NewsModelForm()
    if request.method == "POST":
        obj = NewsModelForm(data=request.POST) ##傳入進行驗證
        if obj.is_valid():
            #models.News.objects.create(**obj.cleaned_data) ##以前Form的時候添加數據要這樣寫
            obj.save()  ##modelform現在可以直接save就可以,save的時候可以保存一對多、多對多的數據
        else:
            print(obj.errors)
    return render(request, "mf.html", locals())

template: [是不需要執行上面的什麽__init__方法、和第二種方法]直接就可以實時了。

<form action="" method="post">
    {% csrf_token %}
    {{ obj.as_p }}
    <input type="submit">
</form>

技術分享圖片

ModelForm的另一個功能:修改

urls:

?
1 re_path(‘edit-(\d+).html‘,views.edit)

views:

class NewsModelForm(ModelForm):
    class Meta:
        model = models.News  ##裏面必須要有一個model,因為他是對每個models類做增刪改查的
        fields =  "__all__"  ##指定了類,要需要指定要處理哪幾個字段 __all__是全部字段
        
def edit(request,nid):
    if request.method == "GET":
        model_obj = models.News.objects.get(id=nid) ##獲取model對象
        obj = NewsModelForm(instance=model_obj)
    else:
        model_obj = models.News.objects.get(id=nid)
        obj = NewsModelForm(request.POST,instance=model_obj)  ##修改的時候是需要instance 的!如果沒有則默認就是增加數據的
        if obj.is_valid():
            # obj.save() ##他裏面的源碼默認是commit=True 會幫你第三張表一起修改了。而在django是可以自己手動指定修改的
            mobj = obj.save(commit=False) #要是commit為False的話,他會返回一個model的對象、點擊commit看源碼
            mobj.save()  ##保存自己表中數據
            mobj.save_m2m() ##修改第三張表中的數據,而你要在這二者之間是可以做,一些你想做的事情的

    return  render(request,"mf.html",locals())

template:

<form action="" method="post">
    {% csrf_token %}
    {{ obj.as_p }}
    <input type="submit">
</form>

ModelForm中Meta的詳解

# from  web import forms
from django.forms import  Form  ##Form要繼承的
from django.forms import ModelForm  ##ModelForm繼承
from  web import models
from django.forms import  widgets as ws
from django import forms
from django.forms import fields

class NewsModelForm(ModelForm):
    email = fields.CharField()  ##這個會叫把原來的email字段覆蓋,變成了CharField的屬性驗證
    pwd  = fields.CharField()  ##還能夠增加一個News中model之外的字段,很有用。的
    class Meta:
        model = models.News  ##裏面必須要有一個model,因為他是對每個models類做增刪改查的
        fields =  "__all__"  ##指定了類,要需要指定要處理哪幾個字段 __all__是全部字段
        #exclude = ["email",]  ##排除某個字段
        labels = {"name":"名字","title":"標題"}  ##顯示字段的label
        help_texts = {"title":"*"}  ##顯示字段的help_texts "*"我們代表為必填
        widgets = {
            "name":ws.Textarea(attrs={"class":"c1"})  ##自定義字段標簽。和加屬性
        }
        error_messages ={
            "email":{"required":"必填","invalid":"格式錯誤"}  ##自定義錯誤提示
        }
        field_classes = {
            "name":forms.EmailField  ##ModelForm的字段是model中的,name在model是CharField的,而我們可以更改他的驗證以郵箱格式進行驗證
        }
        localized_fields = ("ctime",) ##model中是UTC時間,顯示的時候 按本地時間輸出

動態生成ModelForm表單

def Dynamic_Model_Form(admin_class,form_change=True):

    class Meta:
        model = admin_class.model
        fields = "__all__"
        ##排除exclude的字段
        admin_class.form_change = False  ##用戶前端頁面是否生成p標簽的判斷
        if  form_change:
            admin_class.form_change = True
            exclude = admin_class.readonly_fields


    def __new__(cls,*args,**kwargs):
        for field_name in cls.base_fields:   ##字段都包含在了cls.base_fields中
            filed_obj = cls.base_fields[field_name]
            #添加屬性
            filed_obj.widget.attrs.update({class:form-control})  ##
        return ModelForm.__new__(cls)


    DyModelForm = type("Foo",(ModelForm,),{"Meta":Meta,"__new__":__new__})
    return DyModelForm

Model、Form、ModelForm的比較