1. 程式人生 > >django ModelForm

django ModelForm

add mes 舉例 spa log 建議 方便 取數 存在

什麽是ModelForm

Django中Model負責操作數據庫,並且具有簡單的數據庫驗證功能;Form用於用戶請求的驗證,具有強悍的數據庫驗證功能;ModelForm是將二者合二為一,即可用於數據庫操作(部分),也可用於用戶請求的驗證(部分)!

其實Django Admin就是利用ModelForm的功能實現的。

Form組件和ModelForm的區別

ModelForm是Django Model.py和Form組件的結合體,可以簡單/快速使用 Form驗證和數據庫操作功能,但不如Form組件靈活,如果在使用Django做web開發過程中驗證的數據和數據庫字段相關(可以對表進行增、刪、改操,註意 Many to many字段,也可以級聯操作第3張關系表;),建議優先使用ModelForm,用起來更方便些,但是在使用ModelForm的時候慎用fields=‘__all__‘,獲取數據庫所有字段勢必造成性能損耗;

ModelForm應用場景

我們來假設這樣一個場景,假如,我們有一個類需要保存org的一些信息如下面這些字段。這個類我們叫做ChangOrgModel

course_num = models.IntegerField(default=0, verbose_name=u課程數)
students_num = models.IntegerField(default=0, verbose_name=u學習人數)
address = models.CharField(max_length=200, verbose_name=u地址)

如果我們使用form我們需要怎麽做呢。我們大概需要這樣做。

class OrgModelForm(forms.Form):
    course_num = forms.IntegerField()
    students_num = forms.IntegerField()
    address = forms.CharField(max_length=200)

我們不難發現,我們的Form定義跟Model的定義基本沒什麽區別,應該加max_length的時候還是需要加,我們相當於是重寫了一遍,其次,這裏只有三個,並沒有感覺,當有7、8甚至更多的時候,這樣重復操作就很讓人受不了了。所以這裏我們可以使用ModelForm。

定義ModelForm類

from django import forms
from app01 import models
 
class UserModelForm(forms.ModelForm):
    class Meta:
        model = models.User     #關聯的model類
        fields = "__all__"      #或(name,email,user_type)    #驗證哪些字段,"__all__"表示所有字段
        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 = ()   #本地化,根據settings中TIME_ZONE設置的不同時區顯示時間

ModelForm驗證執行的過程

Form所有的鉤子ModelForm都有。

is_valid()-->self.errors-->full_clean()-->self._clean_fields() -->  clean_字段名(自定義方法)
                        self._clean_form() -->  clean(self) 
                        self._post_clean() (整體錯誤)

clean_字段名(自定義方法)

舉例

新建一個項目untitled1,註意: django版本為1.11

修改models.py

from django.db import models

class Depart(models.Model):  # 部門
    caption = models.CharField(verbose_name=部門,max_length=32)

    def __str__(self):
        return self.caption
class Role(models.Model):  # 角色
    title = models.CharField(verbose_name=角色名,max_length=32)

    def __str__(self):
        return self.title

class User(models.Model):  # 用戶

    name = models.CharField(verbose_name=姓名,max_length=32)
    depart = models.ForeignKey(verbose_name=部門,to=Depart,on_delete=models.CASCADE)

    gender_choices = (
        (1,),
        (2,),
    )
    gender = models.IntegerField(verbose_name=性別,choices=gender_choices,default=1)

    roles = models.ManyToManyField(verbose_name=角色,to=Role)

執行命令,生成表

python manage.py makemigrations
python manage.py migrate

修改admin.py,註冊表

from django.contrib import admin
from app01 import models
# Register your models here.

admin.site.register(models.Depart)
admin.site.register(models.Role)
admin.site.register(models.User)

創建超級用戶

登錄admin後臺,錄入數據。這樣做的目的是為了渲染頁面時,不會出現空頁面!

錄入基本數據

修改urls.py,增加路由

from app01 import views

urlpatterns = [
    url(r^admin/, admin.site.urls),
    url(r^user/list/$, views.user_list),
]

修改views.py,增加視圖

from django.shortcuts import render
from app01 import models
# Create your views here.
def user_list(request):
    user_queryset = models.User.objects.all()
    return render(request,user_list.html,{user_queryset:user_queryset})

在templates目錄下,創建文件user_list.html

技術分享圖片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <a href="/user/add/" class="btn btn-primary">添加</a>
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th>名稱</th>
                    <th>性別</th>
                    <th>部門</th>
                    <th>角色</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {% for row in user_queryset %}
                    <tr>
                        <td>{{ row.name }}</td>
                        <td>{{ row.get_gender_display }}</td>
                        <td>{{ row.depart.caption }}</td>
                        <td>
                            {% for node in row.roles.all %}
                                <span>{{ node.title }}</span>
                            {% endfor %}
                        </td>
                        <td>
                            <a href="/user/edit/{{ row.id }}/">編輯</a>
                        </td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</body>
</html>
View Code

訪問頁面:http://127.0.0.1:8000/user/list/技術分享圖片

默認是空的。需要寫添加頁面

添加頁面,怎麽寫呢?寫4個input框?假如有10個表呢?累成狗!

使用ModelForm,根據models.py定義的字段,自動生成input框!

添加功能

修改urls.py,增加路由

技術分享圖片
urlpatterns = [
    url(r^admin/, admin.site.urls),
    url(r^user/list/$, views.user_list),
    url(r^user/add/$, views.user_add),
]
View Code

修改views.py,增加添加頁面,使用ModelForm

需要導入forms組件

技術分享圖片
from django.shortcuts import render,redirect
from app01 import models
from django import forms

# Create your views here.
def user_list(request):
    user_queryset = models.User.objects.all()
    return render(request,user_list.html,{user_queryset:user_queryset})

class UserForm(forms.ModelForm):  # 類名最好是表名+Form
    class Meta:
        model = models.User  # user表
        fields = __all__  # 所有字段
        # fields = [name,depart]
        widgets = {
            # 定義name字段的輸入框為text
            name:forms.TextInput(attrs={class:form-control}),
            # 定義depart字段的輸入框為Select
            depart:forms.Select(attrs={class:form-control}),
            gender:forms.Select(attrs={class:form-control}),
            # 定義roles字段的輸入框為多選框
            roles:forms.SelectMultiple(attrs={class:form-control}),
        }
        # 錯誤信息
        error_messages = {
            # name字段的錯誤信息
            name:{
                # 英文的required轉為中文提示
                required:用戶名不能為空
            }
        }

def user_add(request):
    if request.method == "GET":
        form = UserForm() # 實例化一個空的ModelForm對象
    else:
        form = UserForm(request.POST)  # 接收POST請求數據
        if form.is_valid():  # 進行驗證
            print(通過驗證)
            form.save()  # 將驗證通過的數據插入到數據庫中
            return redirect(/user/list/)  # 重定向頁面
        
    return render(request,user_add.html,{form:form})
View Code

在templates目錄下,創建文件user_add.html

技術分享圖片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
     <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <h1>添加用戶</h1>
        <form method="post" class="form-horizontal" novalidate>
            {% csrf_token %}
            {#for循環form對象#}
            {% for field in form %}
                <div class="form-group">
                    {#顯示字段名#}
                    <label class="col-sm-2 control-label">{{ field.label }} </label>
                    <div class="col-sm-10">
                        {#顯示字段的輸入框以及錯誤信息,0表示取第一個錯誤#}
                      {{ field }} {{ field.errors.0 }}
                    </div>
                  </div>
            {% endfor %}
            <input type="submit" value="提交">
        </form>
    </div>
</body>
</html>
View Code

刷新頁面,點擊添加,效果如下:

技術分享圖片

直接提交空表單,會有錯誤提示

技術分享圖片

添加一個正常數據

技術分享圖片

添加成功後,頁面會自動跳轉

技術分享圖片

修改功能

修改urls.py , 增加路由

urlpatterns = [
    url(r^admin/, admin.site.urls),
    url(r^user/list/$, views.user_list),
    url(r^user/add/$, views.user_add),
    url(r^user/edit/(?P<uid>\d+)/$, views.user_edit),
]

修改views.py,增加視圖

技術分享圖片
from django.shortcuts import render,redirect
from app01 import models
from django import forms

# Create your views here.
def user_list(request):
    user_queryset = models.User.objects.all()
    return render(request,user_list.html,{user_queryset:user_queryset})

class UserForm(forms.ModelForm):  # 類名最好是表名+Form
    class Meta:
        model = models.User  # user表
        fields = __all__  # 所有字段
        # fields = [name,depart]
        widgets = {
            # 定義name字段的輸入框為text
            name:forms.TextInput(attrs={class:form-control}),
            # 定義depart字段的輸入框為Select
            depart:forms.Select(attrs={class:form-control}),
            gender:forms.Select(attrs={class:form-control}),
            # 定義roles字段的輸入框為多選框
            roles:forms.SelectMultiple(attrs={class:form-control}),
        }
        # 錯誤信息
        error_messages = {
            # name字段的錯誤信息
            name:{
                # 英文的required轉為中文提示
                required:用戶名不能為空
            }
        }

def user_add(request):
    if request.method == "GET":
        form = UserForm() # 實例化一個空的ModelForm對象
    else:
        form = UserForm(request.POST)  # 接收POST請求數據
        if form.is_valid():  # 進行驗證
            print(通過驗證)
            form.save()  # 將驗證通過的數據插入到數據庫中
            return redirect(/user/list/)  # 重定向頁面

    return render(request,user_add.html,{form:form})

def user_edit(request,uid):
    # 查詢指定id的數據
    obj = models.User.objects.filter(id=uid).first()
    if request.method ==GET:
        # 賦值instance可以使form表單是可以接受對象的數據
        form = UserForm(instance=obj)
        return render(request,user_edit.html,{form:form})
    else:
        # instance的參數,如果存在那麽save()方法會更新這個實例,否則會創建一個新的實例
        # 由於這裏是更新,如果不指定instance。那麽它會新增一條數據
        # 我們這裏不需要新增,必須要指定instance參數
        form = UserForm(data=request.POST,instance=obj)
        if form.is_valid():
            form.save()  # 更新一條數據
            return redirect(/user/list/)  # 重定向
        else:
            return render(request, user_edit.html, {form: form})
View Code

在templates目錄下,創建文件user_edit.html

技術分享圖片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
     <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
   <h1>編輯用戶</h1>
    <form method="post" novalidate>
        {% csrf_token %}
        {% for field in form %}
            <div>{{ field.label }}{{ field }} {{ field.errors.0 }}</div>
        {% endfor %}
        <input type="submit" value="提交">
    </form>
</div>

</body>
</html>
View Code

刷新頁面,點擊第一條數據後面的編輯,效果如下:

技術分享圖片

上面會自動渲染數據,那麽問題來了,它是如何渲染頁面的?

看views中的一段代碼

if request.method ==GET:
        # 賦值instance可以使form表單是可以接受對象的數據
        form = UserForm(instance=obj)
        return render(request,user_edit.html,{form:form})

這裏的obj就是,表裏面的一條數據,也就是一個對象。那麽執行render時,會渲染這個變量。

看前端頁面代碼

<div>{{ field.label }}{{ field }} {{ field.errors.0 }}</div>

這裏的field,就是字段對象。註意:這個對象是有值的,forms組件會渲染它!

修改一下數據

技術分享圖片

點擊提交,它會自動跳轉

數據就更改了!

技術分享圖片

刪除功能

添加路由

修改user_list.html, 增加刪除按鈕

技術分享圖片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <a href="/user/add/" class="btn btn-primary">添加</a>
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th>名稱</th>
                    <th>性別</th>
                    <th>部門</th>
                    <th>角色</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {% for row in user_queryset %}
                    <tr>
                        <td>{{ row.name }}</td>
                        <td>{{ row.get_gender_display }}</td>
                        <td>{{ row.depart.caption }}</td>
                        <td>
                            {% for node in row.roles.all %}
                                <span>{{ node.title }}</span>
                            {% endfor %}
                        </td>
                        <td>
                            <a href="/user/edit/{{ row.id }}/">編輯</a>
                            <a href="/user/del/{{ row.id }}/">刪除</a>
                        </td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</body>
</html>
View Code

刷新頁面,點擊刪除

技術分享圖片

參考博客

  https://www.cnblogs.com/xiao987334176/p/9524510.html

django ModelForm