返回顶部

Django基础之formset和modelformset

1. formset

顾名思义就是把多个form对象,合并在一起进行批量操作。我在批量分发权限使用过。

举个例子:

第一步:创建form

class MultiAddPermissionForm(forms.Form):
    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(MultiAddPermissionForm, self).__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_id__isnull=True).\
            exclude(menu_id__isnull=True).values_list('id','title')

第二步:实例化formset_factory对象,把我们写的form传进去

generate_formset_class = formset_factory(MultiAddPermissionForm, extra=0)

第三步:通过formset_factory对象,根据请求的不同,进行传值。

如果是GET,就可以通过initial参数,传一个列表生成式包裹的字典,字典里含的值就是form中定义的字段。

如果是POST,就通过data进行赋值,比如把request.POST赋值给他。

generate_name_list = router_name_set - permissions_name_set
        generate_formset = generate_formset_class(
            initial=[row_dict for name, row_dict in all_url_dict.items() if name in generate_name_list]
        )

第四步:进行is_valid判断后的赋值

    if request.method == 'POST' and post_type == 'generate':
        formset = generate_formset_class(data=request.POST)
        if formset.is_valid():
            object_list = []
            post_row_dict = formset.cleaned_data
            has_error = False
            for i in range(0, formset.total_form_count()):
                row_dict = post_row_dict[i]
                try:
                    new_object = models.Permission(**row_dict)
                    new_object.validate_unique()
                    object_list.append(new_object)
                except Exception as e:
                    formset.errors[i].update(e)
                    generate_formset = formset
                    has_error = True
            if not has_error:
                models.Permission.objects.bulk_create(object_list, batch_size=100)
        else:
            generate_formset = formset

第五步: HTML页面中循环generate_formset对象

注意: {{ generate_formset.management_form }} 这句话一定要加上,否则增加不了数据。

<form action="?type=generate" method="post">
            {% csrf_token %}
            {{ generate_formset.management_form }}
            <div class="panel panel-default">
                <!-- Default panel contents -->
                <div class="panel-heading">
                    <i class="fa fa-th-list" aria-hidden="true"></i> 待新建权限列表
                    <button href="#" class="right btn btn-primary btn-xs"
                            style="padding: 2px 8px;margin: -3px;">
                        <i class="fa fa-save" aria-hidden="true"></i>
                        保存
                    </button>
                </div>
                <!-- Table -->
                <table class="table">
                    <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>{{ forloop.counter }}</td>
                            {% for field in  form %}
                                <td>{{ field }}<span style="color: red;">{{ field.errors.0 }}</span></td>
                            {% endfor %}
                        </tr>
                    {% endfor %}

                    </tbody>
                </table>
            </div>
        </form>

 

formset特别注意:

如果是更新操作时,formset怎么知道是哪一个form该更新?

就需要在定义form时,增加一个字段:隐藏的id字段,用于formset校验时,弹出这个id,以此判断是属于哪一个form的数据。

    id = forms.IntegerField(
        widget=forms.HiddenInput()
    )

关于更新操作的代码:

    if request.method == 'POST' and post_type == 'update':
        formset = update_formset_class(data=request.POST)
        if formset.is_valid():
            post_row_dict = formset.cleaned_data
            for i in range(0, formset.total_form_count()):
                row_dict = post_row_dict[i]
                permission_id = row_dict.pop('id')
                try:
                    row_object = models.Permission.objects.filter(pk=permission_id).first()
                    for k,v in row_dict.items():
                        setattr(row_object, k, v)
                    row_object.validate_unique()
                    row_object.save()
                except Exception as e:
                    formset.errors[i].update(e)
                    update_formset = formset
        else:
            update_formset =formset

 

<form action="?type=update" method="post">
            {% csrf_token %}
            {{ update_formset.management_form }}
            <div class="panel panel-default">
                <!-- Default panel contents -->
                <div class="panel-heading">
                    <i class="fa fa-th-list" aria-hidden="true"></i> 待更新权限列表
                    <button href="#" class="right btn btn-primary btn-xs"
                            style="padding: 2px 8px;margin: -3px;">
                        <i class="fa fa-save" aria-hidden="true"></i>
                        保存
                    </button>
                </div>
                <!-- Table -->
                <table class="table">
                    <thead>
                    <tr>
                        <th>序号</th>
                        <th>名称</th>
                        <th>URL</th>
                        <th>别名</th>
                        <th>菜单</th>
                        <th>父权限</th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for form in update_formset %}
                        <tr>
                            <td>{{ forloop.counter }}</td>
                            {% for field in  form %}
                                {% if forloop.first %}
                                    {{ field }}
                                {% else %}
                                    <td>{{ field }}<span style="color: red;">{{ field.errors.0 }}</span></td>
                                {% endif %}
                            {% endfor %}
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </div>
        </form>

 

 

2. modelformset

顾名思义就是把model和多个form集合,进行批量操作

第一步:创建modelform

class StudyRecordModelForm(StarkModelForm):
    class Meta:
        model = models.StudyRecord
        fields = ['record', ]

第二步:初始化modelformset对象:

 

 把我们相应的model和自定义的modelform传进去

 

第三步:根据不同的请求(GET或者是POST),传不同的值

 

 举例:

    def attendance_view(self, request, course_record_id, *args, **kwargs):
        """
        考勤的批量操作
        :param request:
        :param course_record_id:
        :param args:
        :param kwargs:
        :return:
        """
        study_record_object_list = models.StudyRecord.objects.filter(course_record_id=course_record_id)
        study_model_formset = modelformset_factory(models.StudyRecord, form=StudyRecordModelForm, extra=0)

        if request.method == 'POST':
            formset = study_model_formset(queryset=study_record_object_list, data=request.POST)
            if formset.is_valid():
                formset.save()
            return render(request, 'attendance.html', {'formset': formset})

        formset = study_model_formset(queryset=study_record_object_list)
        return render(request, 'attendance.html', {'formset': formset})

 

第四步:HTML页面使用

{% extends 'layout.html' %}


{% block content %}
    <div class="luffy-container">
        <form method="post">
            {% csrf_token %}
            {{ formset.management_form }}
            <div style="margin: 5px 0">
                <input type="submit" value="保存" class="btn btn-success">
            </div>
            <table class="table table-bordered">
                <thead>
                <tr>
                    <th>姓名</th>
                    <th>考勤</th>
                </tr>
                </thead>
                <tbody>
                {% for form in formset %}
                    <tr>
                        {{ form.id }}
                        <td>{{ form.instance.student }}</td>
                        <td>{{ form.record }} <span style="color: red">{{ form.record.errors.0 }}</span></td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </form>
    </div>
{% endblock %}

 特别注意:

1.循环每一个form对象的时候,一定要加上{{ form.id }}

posted @ 2020-10-13 23:34  muguangrui  阅读(140)  评论(0编辑  收藏  举报