Django(九)上:ModelForm操做

1、內容回顧

 Model
        - 數據庫操做
        - 驗證
        class A(MOdel):
            user = 
            email = 
            pwd = 

    Form
        - class LoginForm(Form): 
            email = fields.EmailField()
            user = 
            pwd = 

        - is_valid -> 每個字段進行正則(字段內置正則)+clean_字段 -> clean(__all__)  -> _post_clean
        - cleand_data
        - error
    --------> 推薦 <---------

 

2、ModelForm操做及驗證

Model + Form ==> ModelForm。model和form的結合體,因此有如下功能:html

  • 驗證
  • 數據庫操做

model有操做數據庫的字段,form驗證也有那幾個字段,雖然耦合度下降,可是代碼是有重複的。若是利用model裏的字段,那是否是form裏的字段就不用寫了。html5

一、Model + Form (以前的操做)

models.pymysql

class UserType(models.Model):
    caption = models.CharField(max_length=32)

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    email = models.EmailField()
    user_type = models.ForeignKey(to='UserType',to_field='id')

 

forms.pysql

from django import forms
from django.forms import fields

class UserInfoForm(forms.Form):
    # username = models.CharField(max_length=32)    <-- models
    username = fields.CharField(max_length=32)
    # email = models.EmailField()    <-- models
    email = fields.EmailField()
    # user_type = models.ForeignKey(to='UserType',to_field='id')    <-- models
    user_type = fields.ChoiceField(
        choices=models.UserType.objects.values_list('id','caption')
    )

    # 下面的操做是讓數據在網頁上實時更新。
    def __init__(self, *args, **kwargs):
        super(UserInfoForm,self).__init__(*args, **kwargs)
        self.fields['user_type'].choices = models.UserType.objects.values_list('id','caption')

 

index.html數據庫

<body>
    <form action="/index/" method="POST" novalidate="novalidate">
        {% csrf_token %}
        {{ obj.as_p }}
        <input type="submit" value="提交">
    </form>
</body>

 

novalidate 注: HTML5輸入類型和瀏覽器驗證django

若是表單中包含URLField、EmailField和其餘整數字段相似,Django將使用url、email和number這樣的html5輸入類型。默認狀況下,瀏覽器可能會對這些字段進行他們自身的驗證,這些驗證可能比Django的驗證更嚴格。若是你想禁用這個行爲,請設置form標籤的novalidate屬性,或者制定一個不一樣的字段,如TextInput。json

二、ModelForm 基本操做

下面經過modelform實現小程序

forms.py瀏覽器

class UserInfoModelForm(forms.ModelForm):

    class Meta:
        model = models.UserInfo    # 與models創建了依賴關係
        fields = "__all__"

 

views.pymarkdown

def index(request):
    if request.method == "GET":
        obj = UserInfoModelForm()
        return render(request,"index.html",{'obj':obj})
    elif request.method == "POST":
        obj = UserInfoModelForm(request.POST)
        print(obj.is_valid())  # 這是方法,別忘記了加括號
        print(obj.cleaned_data)
        print(obj.errors)
        return render(request,"index.html",{'obj':obj})

 

自定製字段名

如何定義http上定義的字段呢,自定義寫成中文的?以前的用法是在Form裏寫上label。Model Form定義要用verbose_name

models.py

class UserInfo(models.Model):
    username = models.CharField(max_length=32, verbose_name='用戶')
    email = models.EmailField(verbose_name='郵箱')
    user_type = models.ForeignKey(to='UserType',to_field='id', verbose_name='類型')

 

若是不在model裏定義,在modelForm裏實現,利用labels

class UserInfoModelForm(forms.ModelForm):

    class Meta:
        model = models.UserInfo
        fields = "__all__"
        labels = {
            'username':'用戶名',
            'email':'郵箱',
        }

 

展現指定的列

fields = "__all__"上面展現全部的,可不能夠展現指定的列

fields = ['username','email']   # 顯示指定列
exclude = ['username']          # 排除指定列

 

爲何modelForm裏也能作驗證?

form裏面有is_validcleaned_dataerrors

# Form驗證:
    UserInfoForm -> Form -> BaseForm( 包含is_valid等方法)

# ModelForm驗證:
    UserInfoModelForm -> ModelForm -> BaseModelForm -> BaseForm

 

三、ModelForm組件

ModelForm
    a.  class Meta:
            model,                           # 對應Model的
            fields=None,                     # 字段
            exclude=None,                    # 排除字段
            labels=None,                     # 提示信息
            help_texts=None,                 # 幫助提示信息
            widgets=None,                    # 自定義插件
            error_messages=None,             # 自定義錯誤信息(總體錯誤信息from django.core.exceptions import NON_FIELD_ERRORS)
            field_classes=None               # 自定義字段類 (也能夠自定義字段)
            localized_fields=('birth_date',) # 本地化,如:根據不一樣時區顯示數據
            如:
                數據庫中
                    2016-12-27 04:10:57
                setting中的配置
                    TIME_ZONE = 'Asia/Shanghai'
                    USE_TZ = True
                則顯示:
                    2016-12-27 12:10:57
    b. 驗證執行過程
        is_valid -> full_clean -> 鉤子 -> 總體錯誤

    c. 字典字段驗證
        def clean_字段名(self):
            # 能夠拋出異常
            # from django.core.exceptions import ValidationError
            return "新值"
    d. 用於驗證
        model_form_obj = XXOOModelForm()
        model_form_obj.is_valid()
        model_form_obj.errors.as_json()
        model_form_obj.clean()
        model_form_obj.cleaned_data
    e. 用於建立
        model_form_obj = XXOOModelForm(request.POST)
        #### 頁面顯示,並提交 #####
        # 默認保存多對多
            obj = form.save(commit=True)
        # 不作任何操做,內部定義 save_m2m(用於保存多對多)
            obj = form.save(commit=False)
            obj.save()      # 保存單表信息
            obj.save_m2m()  # 保存關聯多對多信息

    f. 用於更新和初始化
        obj = model.tb.objects.get(id=1)
        model_form_obj = XXOOModelForm(request.POST,instance=obj)
        ...

        PS: 單純初始化
            model_form_obj = XXOOModelForm(initial={...})

 

注意:導入模塊名(fields、widgets)和字段名重複,因此導入時要起個別名。

from django import forms
from django.forms import fields as Ffields
from django.forms import widgets as Fwidgets
class UserInfoModelForm(forms.ModelForm):

    is_rmb = Ffields.CharField(widget=Fwidgets.CheckboxInput())

    class Meta:
        model = models.UserInfo
        fields = '__all__'
        # fields =  ['username','email']
        # exclude = ['username']
        labels = {
            'username': '用戶名',
            'email': '郵箱',
        }
        help_texts = {
            'username': '...'
        }
        widgets = {
            'username': Fwidgets.Textarea(attrs={'class': 'c1'})
        }
        error_messages = {
            '__all__':{    # 總體錯誤信息

            },
            'email': {
                'required': '郵箱不能爲空',
                'invalid': '郵箱格式錯誤..',
            }
        }
        field_classes = {  # 定義字段的類是什麼
            # 'email': Ffields.URLField  # 這裏只能填類,加上括號就是對象了。
        }

        # localized_fields=('ctime',)  # 哪些字段作本地化

 

四、ModelForm 數據庫操做

1.一、建立數據save

若是數據驗證是ok的,那麼save,就直接在數據庫中建立完數據了

if obj.is_valid():
    obj.save()      # 建立數據

 

在以下一對多、多對多關係中:

class UserType(models.Model):
    caption = models.CharField(max_length=32)

class UserGroup(models.Model):
    name = models.CharField(max_length=32)

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    email = models.EmailField()
    user_type = models.ForeignKey(to='UserType',to_field='id')
    u2g = models.ManyToManyField(UserGroup)

 

這樣的話,執行上面的obj.save()會在UserInfo表和多對多關係表裏都增長數據。

views.py

def index(request):
    if request.method == "GET":
        obj = UserInfoModelForm()
        return render(request,'index.html',{'obj': obj})
    elif request.method == "POST":
        obj = UserInfoModelForm(request.POST)
        if obj.is_valid():
            obj.save()  # 等價如下三句
            # instance = obj.save(False)
            # instance.save()
            # obj.save_m2m()
        return render(request,'index.html',{'obj': obj})

 

1.二、save 作了哪些操做?

save源碼裏:

def save(self, commit=True):
    """"""
    if commit:
        self.instance.save()    # 指的當前model對象
        self._save_m2m()        # 指:保存m2m對象
    else:
        self.save_m2m = self._save_m2m
    return self.instance    # model 類的對象
    """"""

 

因此instance = obj.save(False)時,什麼都不會操做。

if obj.is_valid():
    instance = obj.save(False)
    instance.save()     # 當前對象表數據建立
    obj.save_m2m()      # 多對多表數據建立
    # 上面這三句完成的是和上面 obj.save 同樣的操做。拆開就能夠自定製操做了

 

二、修改數據

修改表數據是,記得把instance信息也傳進去,否則是新建數據,而不是對某行數據進行修改。

編輯用戶信息,新url方式保留默認數據

urls.py

url(r'^user_list/', views.user_list),
url(r'^edit-(\d+)/', views.user_edit),

 

views.py

def user_list(request):
    li = models.UserInfo.objects.all().select_related('user_type')  # 這裏只能是外鍵,多對多字段也不能夠
    return render(request,'user_list.html',{'li': li})

def user_edit(request, nid):
    # 獲取當前id對象的用戶信息
    # 顯示用戶已經存在數據
    if request.method == "GET":
        user_obj = models.UserInfo.objects.filter(id=nid).first()
        mf = UserInfoModelForm(instance=user_obj)   # 把默認數據傳遞進去
        return render(request,'user_edit.html',{'mf': mf, 'nid': nid})
    elif request.method == 'POST':
        # 數據修改的信息,給數據庫的哪一行作修改?
        user_obj = models.UserInfo.objects.filter(id=nid).first()
        mf = UserInfoModelForm(request.POST,instance=user_obj)  # 指定給誰作修改
        if mf.is_valid():
            mf.save()
        else:
            print(mf.errors.as_json())
        return render(request,'user_edit.html',{'mf': mf, 'nid': nid})

 

user_list.html

<body>
    <ul>
        {% for row in li %}
            <li>{{ row.username }} - {{ row.user_type.caption }} - <a href="/edit-{{ row.id }}/">編輯</a></li>
        {% endfor %}
    </ul>
</body>

 

user_edit.html

<body>
    <form method="POST" action="/edit-{{ nid }}/">
        {% csrf_token %}
    {{ mf.as_p }}
        <input type="submit" value="提交" />
    </form>
</body>

 

五、ModelForm鉤子、額外字段

數據驗證鉤子

從上面的Form和ModelForm中,他們都是繼承了BaseForm,而is_valid是在BaseForm中定義的,因此ModelForm也能和Form同樣使用各類鉤子

額外字段

像網頁上的checkbox,一個月內免登錄,用提交到數據庫麼?這個只須要設置session和cookie就能夠了。

views.py

class UserInfoModelForm(forms.ModelForm):

    is_rmb = fields.CharField(widget=widgets.CheckboxInput())  # 額外字段

    class Meta:
        model = models.UserInfo
        fields = '__all__'

 

六、總結

  1. 生成HTML標籤:class Meta: ...
    2. mf = xxxModelForm(instance=ModelObj) 生成默認值
    3. 額外的標籤, is_rmb = Ffields.CharField(widget=Fwidgets.CheckboxInput())
    4. 各類驗證 is_valid() -> 各類鉤子...
    5.  mf.save()
        #
        instance = mf.save(False)
        instance.save()
        mf.save_m2m()

 

ModelForm由於model和form耦合太密切,因此通常寫小程序用它。


轉載請務必保留此出處:http://www.cnblogs.com/lgeng/articles/7366434.html

 

<!-- END  -->

 

 

 

 

 

《版本說明》: 本文轉自 -- http://blog.csdn.net/fgf00/article/details/54917396

相關文章
相關標籤/搜索