1. 程式人生 > >Django form modelform formset modelformset的各種用法和區別

Django form modelform formset modelformset的各種用法和區別

首先上結論:

  form適用於對單個表單的操作,並且需要對每個欄位的驗證規則自定義。

  modelform:適用於對使用者提交的單個表單操作,欄位可以用model中的表的欄位來作為驗證規則,適用於快速的進行增加、修改。

  formset:適用於對多個表單進行操作,欄位需要也可以用model中的表的欄位來作為驗證規則。

  modelfoemset:適用於對多個表單進行操作,欄位需要也可以用model中的表的欄位來作為驗證規則,速度可能快一些(有待考證)

 form用法

後端程式碼

複製程式碼
from django.forms import
widgets wid_01=widgets.TextInput(attrs={"class":"form-control"}) wid_02=widgets.PasswordInput(attrs={"class":"form-control"}) class UserForm(forms.Form): name=forms.CharField(max_length=32, widget=wid_01 ) pwd=forms.CharField(max_length=32,widget=wid_02) r_pwd
=forms.CharField(max_length=32,widget=wid_02) email=forms.EmailField(widget=wid_01) tel=forms.CharField(max_length=32,widget=wid_01) def register(request): if request.method=="POST": form=UserForm(request.POST) if form.is_valid(): print(form.cleaned_data) #
所有乾淨的欄位以及對應的值 else: print(form.cleaned_data) # print(form.errors) # ErrorDict : {"校驗錯誤的欄位":["錯誤資訊",]} print(form.errors.get("name")) # ErrorList ["錯誤資訊",] return HttpResponse("OK") form=UserForm() return render(request,"register.html",locals())
複製程式碼

 

複製程式碼
<form action="" method="post">
                    {% csrf_token %}
                    
                    {% for field in form %}
                        <div>
                            <label for="">{{ field.label }}</label>
                            {{ field }}
                        </div>
                    {% endfor %}
                    <input type="submit" class="btn btn-default pull-right">
                
</form>
複製程式碼

 modelform用法

複製程式碼
class MenuModelForm(forms.ModelForm):
    class Meta:
        model = models.Menu
        fields = '__all__'
        widgets = {
            'title': forms.TextInput(attrs={'placeholder': '請輸入角色名稱', 'class': 'form-control'}),
            'icon': forms.RadioSelect(
                choices=ICON_LIST
            )
        }
        error_messages = {
            'title': {
                'required': '選單名稱不能為空',
            },
            'icon': {
                'required': '請選擇圖示',
            }
        }



def menu_add(request):
    """
    新增選單
    :param request:
    :return:
    """
    if request.method == 'GET':
        form = MenuModelForm()
    else:
        form = MenuModelForm(request.POST)
        if form.is_valid():
            print(form.data)
            form.save()
            return redirect(reverse('rbac:menu_list'))
    return render(request, 'rbac/menu_change.html', {'form': form})


def menu_edit(request, pk):
    """
    編輯選單
    :param request:
    :return:
    """
    obj = models.Menu.objects.filter(id=pk).first()
    if not obj:
        return HttpResponse('選單不存在')

    if request.method == 'GET':
        form = MenuModelForm(instance=obj)
        return render(request, 'rbac/menu_change.html', {'form': form})

    form = MenuModelForm(request.POST, instance=obj)
    if form.is_valid():
        form.save()
        return redirect(reverse('rbac:menu_list'))
複製程式碼

 

 

複製程式碼
<div class="luffy-container">
        <form class="form-horizontal" method="post" novalidate>
            {% csrf_token %}
            <div class="form-group">
                <label class="col-sm-2 control-label">選單名稱:</label>
                <div class="col-sm-6">
                    {{ form.title }}

                </div>
                <div class="col-sm-offset-1 col-sm-2">
                    <input type="submit" value="保 存" class="btn btn-primary">
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-2 control-label">圖示:</label>
                <div class="col-sm-7 icon-area">
                    {{ form.icon }}
                </div>
                <div class="col-sm-3">
                    <div>{{ form.title.errors.0 }}</div>
                    <div>{{ form.icon.errors.0 }}</div>
                </div>
            </div>

        </form>
    </div>
複製程式碼

formset用法

 

 

複製程式碼
class MultiPermissionForm(forms.Form):
    id = forms.IntegerField(
        widget=forms.HiddenInput(),
        required=False
    )
    title = forms.CharField(
        widget=forms.TextInput(attrs={'class': "form-control"})
    )
    url = forms.CharField(
        widget=forms.TextInput(attrs={'class': "form-control"})
    )
    name = forms.CharField(
        widget=forms.TextInput(attrs={'class': "form-control"})
    )
    menu_id = forms.ChoiceField(
        choices=[(None, '-----')],
        widget=forms.Select(attrs={'class': "form-control"}),
        required=False,

    )

    pid_id = forms.ChoiceField(
        choices=[(None, '-----')],
        widget=forms.Select(attrs={'class': "form-control"}),
        required=False,
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['menu_id'].choices += models.Menu.objects.values_list('id', 'title')
        self.fields['pid_id'].choices += models.Permission.objects.filter(pid__isnull=True).exclude(
            menu__isnull=True).values_list('id', 'title')

    def clean_pid_id(self):
        menu = self.cleaned_data.get('menu_id')
        pid = self.cleaned_data.get('pid_id')
        if menu and pid:
            raise forms.ValidationError('選單和根許可權同時只能選擇一個')
        return pid

def add(request):
    """
      增加
    :param request:
    :return:
    """
    MultiPermissionFormSet = formset_factory(MultiPermissionForm, extra=0)
    if request.method == 'GET':
        form = MultiPermissionFormSet()
        return render(request,'list.html',{'form':form}
    form =    MultiPermissionFormSet(request.post)
    if form.is_valid():
        return redict('url') 
    else:
         .........

def edit(request):
    """
      修改
    :param request:
    :return:
    """
    MultiPermissionFormSet = formset_factory(MultiPermissionForm, extra=0)
    if request.method == 'GET':
        form = MultiPermissionFormSet('資料')
        # formset是支援批量修改的所以 這裡傳入的資料一定要是可迭代物件,並且裡面的 
          資料型別是字典或者物件
        return render(request,'list.html',{'form':form}
    form =    MultiPermissionFormSet(request.post)
    if form.is_valid():
        return redict('url') 
    else:
         .........
複製程式碼

 

 

複製程式碼
<div class="luffy-container">
        <form method="post" action="?type=generate">
            {% csrf_token %}
            {{ form.management_form }}
    # 這裡一定要記得寫這一步
            <div class="panel panel-default">
                <!-- Default panel contents -->
                <div class="panel-heading">
                    <i class="fa fa-binoculars" aria-hidden="true"></i> 待新建許可權列表
                    <button class="right btn btn-primary btn-xs" style="padding: 2px 8px;margin: -3px;">
                        <i class="fa fa-save" aria-hidden="true"></i>
                        新建
                    </button>
                </div>
                <div class="panel-body" style="color: #9d9d9d;">
                    注意:路由系統中自動發現且資料庫中不存在的路由。
                </div>

                <table class="table table-bordered">
                    <thead>
                    <tr>
                        <th>序號</th>
                        <th>名稱</th>
                        <th>URL</th>
                        <th>別名</th>
                        <th>所屬選單</th>
                        <th>根許可權</th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for form in generate_formset %}
    
                        <tr>
                        <td style="vertical-align: middle;">{{ forloop.counter }}</td>
                        {% for field in form %}  #  這裡在渲染標籤的時候一定要渲染 id這個標籤  這個很重要不然會報錯  這裡是寫了兩個fou迴圈 所以自動渲染了id標籤
                            {% if forloop.first %}
                                <td class="hide">
                                    {% else %}
                                <td>
                            {% endif %}
                        {{ field }}<span style="color: firebrick;">{{ field.errors.0 }}</span>
                        </td>
                        {% endfor %}
                    {% endfor %}
                    </tbody>
                </table>
            </div>
        </form>    
複製程式碼

 

 modelformset用法

複製程式碼
class StudyRecordModelForm(forms.ModelForm):
    class Meta:
        model = models.StudyRecord
        fields = ['student','record','score','homework_note']


 def changelist_view(self,request):
        ccid = request.GET.get('ccid')
        model_formset_cls = modelformset_factory(models.StudyRecord,StudyRecordModelForm,extra=0)
        queryset = models.StudyRecord.objects.filter(course_record_id=ccid)
        if request.method == "GET":
            formset = model_formset_cls(queryset=queryset)
        # 這裡UI定是個可迭代物件,因為modelformset是操作多表的,裡面的資料型別可以為字典或者物件
return render(request,'study_record.html',{'formset':formset}) formset = model_formset_cls(data=request.POST) print(request.POST) if formset.is_valid(): formset.save() return redirect('/stark/crm/studyrecord/list/?ccid=%s' %ccid ) return render(request, 'study_record.html', {'formset': formset})
複製程式碼

 

複製程式碼
  <div class="panel panel-default">
        <div class="panel-heading">學習記錄</div>
        <div class="panel-body">
            <div style="width: 680px;margin: 0 auto;">
                <form method="post">
                    {% csrf_token %}
                    {{ formset.management_form }}
                   # 這裡一定要加這句程式碼

                    <table class="table table-bordered">
                        <thead>
                        <tr>
                            <th>姓名</th>
                            <th>考勤</th>
                            <th>作業成績</th>
                            <th>作業評語</th>
                        </tr>
                        </thead>
                        <tbody>
                        {% for form in formset %}
                            <tr>
                                {{ form.id }}  
                           # 這裡只寫了一層for迴圈,所以手動寫欄位,必須把id欄位寫上
                                <td>{{ form.instance.student }}</td>
                                <td>{{ form.record }} {{ form.record.errors.0 }}</td>
                                <td>{{ form.score }} {{ form.score.errors.0 }}</td>
                                <td>{{ form.homework_note }} {{ form.homework_note.errors.0 }}</td>
                            </tr>
                        {% endfor %}
                        </tbody>
                    </table>
                    <input type="submit" value="儲存">
                </form>
            </div>
        </div>
    </div>                        
複製程式碼

 

以上為這四種的區別和用法。

 

首先上結論:

  form適用於對單個表單的操作,並且需要對每個欄位的驗證規則自定義。

  modelform:適用於對使用者提交的單個表單操作,欄位可以用model中的表的欄位來作為驗證規則,適用於快速的進行增加、修改。

  formset:適用於對多個表單進行操作,欄位需要也可以用model中的表的欄位來作為驗證規則。

  modelfoemset:適用於對多個表單進行操作,欄位需要也可以用model中的表的欄位來作為驗證規則,速度可能快一些(有待考證)

 form用法

後端程式碼

複製程式碼
from django.forms import widgets

wid_01=widgets.TextInput(attrs={"class":"form-control"})
wid_02=widgets.PasswordInput(attrs={"class":"form-control"})

class UserForm(forms.Form):
    name=forms.CharField(max_length=32,
                         widget=wid_01
                         )
    pwd=forms.CharField(max_length=32,widget=wid_02)
    r_pwd=forms.CharField(max_length=32,widget=wid_02)
    email=forms.EmailField(widget=wid_01)
    tel=forms.CharField(max_length=32,widget=wid_01)



def register(request):

    if request.method=="POST":
        form=UserForm(request.POST)
        if form.is_valid():
            print(form.cleaned_data)       # 所有乾淨的欄位以及對應的值
        else:
            print(form.cleaned_data)       #
            print(form.errors)             # ErrorDict : {"校驗錯誤的欄位":["錯誤資訊",]}
            print(form.errors.get("name")) # ErrorList ["錯誤資訊",]
        return HttpResponse("OK")
    form=UserForm()
    return render(request,"register.html",locals())
複製程式碼

 

複製程式碼
<form action="" method="post">
                    {% csrf_token %}
                    
                    {% for field in form %}
                        <div>
                            <label for="">{{ field.label }}</label>
                            {{ field }}
                        </div>
                    {% endfor %}
                    <input type="submit" class="btn btn-default pull-right">
                
</form>
複製程式碼

 modelform用法

複製程式碼
class MenuModelForm(forms.ModelForm):
    class Meta:
        model = models.Menu
        fields = '__all__'
        widgets = {
            'title': forms.TextInput(attrs={'placeholder': '請輸入角色名稱', 'class': 'form-control'}),
            'icon': forms.RadioSelect(
                choices=ICON_LIST
            )
        }
        error_messages = {
            'title': {
                'required': '選單名稱不能為空',
            },
            'icon': {
                'required': '請選擇圖示',
            }
        }



def menu_add(request):
    """
    新增選單
    :param request:
    :return:
    """
    if request.method == 'GET':
        form = MenuModelForm()
    else:
        form = MenuModelForm(request.POST)
        if form.is_valid():
            print(form.data)
            form.save()
            return redirect(reverse('rbac:menu_list'))
    return render(request, 'rbac/menu_change.html', {'form': form})


def menu_edit(request, pk):
    """
    編輯選單
    :param request:
    :return:
    """
    obj = models.Menu.objects.filter(id=pk).first()
    if not obj:
        return HttpResponse('選單不存在')

    if request.method == 'GET':
        form = MenuModelForm(instance=obj)
        return render(request, 'rbac/menu_change.html', {'form': form})

    form = MenuModelForm(request.POST, instance=obj)
    if form.is_valid():
        form.save()
        return redirect(reverse('rbac:menu_list'))
複製程式碼

 

 

複製程式碼
<div class="luffy-container">
        <form class="form-horizontal" method="post" novalidate>
            {% csrf_token %}
            <div class="form-group">
                <label class="col-sm-2 control-label">選單名稱:</label>
                <div class="col-sm-6">
                    {{ form.title }}

                </div>
                <div class="col-sm-offset-1 col-sm-2">
                    <input type="submit" value="保 存" class="btn btn-primary">
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-2 control-label">圖示:</label>
                <div class="col-sm-7 icon-area">
                    {{ form.icon }}
                </div>
                <div class="col-sm-3">
                    <div>{{ form.title.errors.0 }}</div>
                    <div>{{ form.icon.errors.0 }}</div>
                </div>
            </div>

        </form>
    </div>
複製程式碼

formset用法

 

 

複製程式碼
class MultiPermissionForm(forms.Form):
    id = forms.IntegerField(
        widget=forms.HiddenInput(),
        required=False
    )
    title = forms.CharField(
        widget=forms.TextInput(attrs={'class': "form-control"})
    )
    url = forms.CharField(
        widget=forms.TextInput(attrs={'class': "form-control"})
    )
    name = forms.CharField(
        widget=forms.TextInput(attrs={'class': "form-control"})
    )
    menu_id = forms.ChoiceField(
        choices=[(None, '-----')],
        widget=forms.Select(attrs={'class': "form-control"}),
        required=False,

    )

    pid_id = forms.ChoiceField(
        choices=[(None, '-----')],
        widget=forms.Select(attrs={'class': "form-control"}),
        required=False,
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['menu_id'].choices += models.Menu.objects.values_list('id', 'title')
        self.fields['pid_id'].choices += models.Permission.objects.filter(pid__isnull=True).exclude(
            menu__isnull=True).values_list('id', 'title')

    def clean_pid_id(self):
        menu = self.cleaned_data.get('menu_id')
        pid = self.cleaned_data.get('pid_id')
        if menu and pid:
            raise forms.ValidationError('選單和根許可權同時只能選擇一個')
        return pid

def add(request):
    """
      增加
    :param request:
    :return:
    """
    MultiPermissionFormSet = formset_factory(MultiPermissionForm, extra=0)
    if request.method == 'GET':
        form = MultiPermissionFormSet()
        return render(request,'list.html',{'form':form}
    form =    MultiPermissionFormSet(request.post)
    if form.is_valid():
        return redict('url') 
    else:
         .........

def edit(request):
    """
      修改
    :param request:
    :return:
    """
    MultiPermissionFormSet = formset_factory(MultiPermissionForm, extra=0)
    if request.method == 'GET':
        form = MultiPermissionFormSet('資料')
        # formset是支援批量修改的所以 這裡傳入的資料一定要是可迭代物件,並且裡面的 
          資料型別是字典或者物件
        return render(request,'list.html',{'form':form}
    form =    MultiPermissionFormSet(request.post)
    if form.is_valid():
        return redict('url') 
    else:
         .........
複製程式碼

 

 

複製程式碼
<div class="luffy-container">
        <form method="post" action="?type=generate">
            {% csrf_token %}
            {{ form.management_form }}
    # 這裡一定要記得寫這一步
            <div class="panel panel-default">
                <!-- Default panel contents -->
                <div class="panel-heading">
                    <i class="fa fa-binoculars" aria-hidden="true"></i> 待新建許可權列表
                    <button class="right btn btn-primary btn-xs" style="padding: 2px 8px;margin: -3px;">
                        <i class="fa fa-save" aria-hidden="true"></i>
                        新建
                    </button>
                </div>
                <div class="panel-body" style="color: #9d9d9d;">
                    注意:路由系統中自動發現且資料庫中不存在的路由。
                </div>

                <table class="table table-bordered">
                    <thead>
                    <tr>
                        <th>序號</th>
                        <th>名稱</th>
                        <th>URL</th>
                        <th>別名</th>
                        <th>所屬選單</th>
                        <th>根許可權</th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for form in generate_formset %}
    
                        <tr>
                        <td style="vertical-align: middle;">{{ forloop.counter }}</td>
                        {% for field in form %}  #  這裡在渲染標籤的時候一定要渲染 id這個標籤  這個很重要不然會報錯  這裡是寫了兩個fou迴圈 所以自動渲染了id標籤
                            {% if forloop.first %}
                                <td class="hide">
                                    {% else %}
                                <td>
                            {% endif %}
                        {{ field }}<span style="color: firebrick;">{{ field.errors.0 }}</span>
                        </td>
                        {% endfor %}
                    {% endfor %}
                    </tbody>
                </table>
            </div>
        </form>    
複製程式碼

 

 modelformset用法

複製程式碼
class StudyRecordModelForm(forms.ModelForm):
    class Meta:
        model = models.StudyRecord
        fields = ['student','record','score','homework_note']


 def changelist_view(self,request):
        ccid = request.GET.get('ccid')
        model_formset_cls = modelformset_factory(models.StudyRecord,StudyRecordModelForm,extra=0)
        queryset = models.StudyRecord.objects.filter(course_record_id=ccid)
        if request.method == "GET":
            formset = model_formset_cls(queryset=queryset)
        # 這裡UI定是個可迭代物件,因為modelformset是操作多表的,裡面的資料型別可以為字典或者物件
return render(request,'study_record.html',{'formset':formset}) formset = model_formset_cls(data=request.POST) print(request.POST) if formset.is_valid(): formset.save() return redirect('/stark/crm/studyrecord/list/?ccid=%s' %ccid ) return render(request, 'study_record.html', {'formset': formset})
複製程式碼

 

複製程式碼
  <div class="panel panel-default">
        <div class="panel-heading">學習記錄</div>
        <div class="panel-body">
            <div style="width: 680px;margin: 0 auto;">
                <form method="post">
                    {% csrf_token %}
                    {{ formset.management_form }}
                   # 這裡一定要加這句程式碼

                    <table class="table table-bordered">
                        <thead>
                        <tr>
                            <th>姓名</th>
                            <th>考勤</th>
                            <th>作業成績</th>
                            <th>作業評語</th>
                        </tr>
                        </thead>
                        <tbody>
                        {% for form in formset %}
                            <tr>
                                {{ form.id }}  
                           # 這裡只寫了一層for迴圈,所以手動寫欄位,必須把id欄位寫上
                                <td>{{ form.instance.student }}</td>
                                <td>{{ form.record }} {{ form.record.errors.0 }}</td>
                                <td>{{ form.score }} {{ form.score.errors.0 }}</td>
                                <td>{{ form.homework_note }} {{ form.homework_note.errors.0 }}</td>
                            </tr>
                        {% endfor %}
                        </tbody>
                    </table>
                    <input type="submit" value="儲存">
                </form>
            </div>
        </div>
    </div>                        
複製程式碼

 

以上為這四種的區別和用法。