首页 > 技术文章 > Django【二十】批量更新

youxiu123 2019-09-29 08:26 原文

1、表结构

class Student(models.Model):
    """
    学生表(已报名)
    """
    customer = models.OneToOneField(verbose_name='客户信息', to='Customer',on_delete=models.CASCADE,null=True,blank=True)
    class_list = models.ManyToManyField(verbose_name="已报班级", to='ClassList', blank=True,related_name="students")

    emergency_contract = models.CharField(max_length=32, blank=True, null=True, verbose_name='紧急联系人')
    company = models.CharField(verbose_name='公司', max_length=128, blank=True, null=True)
    location = models.CharField(max_length=64, verbose_name='所在区域', blank=True, null=True)
    position = models.CharField(verbose_name='岗位', max_length=64, blank=True, null=True)
    salary = models.IntegerField(verbose_name='薪资', blank=True, null=True)
    welfare = models.CharField(verbose_name='福利', max_length=256, blank=True, null=True)
    date = models.DateField(verbose_name='入职时间', help_text='格式yyyy-mm-dd', blank=True, null=True)
    memo = models.CharField(verbose_name='备注', max_length=256, blank=True, null=True)

    def __str__(self):
        return self.customer.name


class ClassStudyRecord(models.Model):
    """
    上课记录表 (班级记录)
    """
    class_obj = models.ForeignKey(verbose_name="班级", to="ClassList",on_delete=models.CASCADE)
    day_num = models.IntegerField(verbose_name="节次", help_text=u"此处填写第几节课或第几天课程...,必须为数字")

    teacher = models.ForeignKey(verbose_name="讲师", to='UserInfo',on_delete=models.CASCADE)
    date = models.DateField(verbose_name="上课日期", auto_now_add=True)
    course_title = models.CharField(verbose_name='本节课程标题', max_length=64, blank=True, null=True)
    course_memo = models.TextField(verbose_name='本节课程内容概要', blank=True, null=True)
    has_homework = models.BooleanField(default=True, verbose_name="本节有作业",)
    homework_title = models.CharField(verbose_name='本节作业标题', max_length=64, blank=True, null=True)
    homework_memo = models.TextField(verbose_name='作业描述', max_length=500, blank=True, null=True)
    exam = models.TextField(verbose_name='踩分点', max_length=300, blank=True, null=True)

    def __str__(self):
        return "{0} day{1}".format(self.class_obj, self.day_num)

class StudentStudyRecord(models.Model):
    '''
    学生学习记录
    '''
    student = models.ForeignKey(verbose_name="学员", to='Student',on_delete=models.CASCADE)
    classstudyrecord = models.ForeignKey(verbose_name="第几天课程", to="ClassStudyRecord",on_delete=models.CASCADE)
    record_choices = (('checked', "已签到"),
                      ('vacate', "请假"),
                      ('late', "迟到"),
                      ('noshow', "缺勤"),
                      ('leave_early', "早退"),
                      )

    record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64)
    score_choices = ((100, 'A+'),
                     (90, 'A'),
                     (85, 'B+'),
                     (80, 'B'),
                     (70, 'B-'),
                     (60, 'C+'),
                     (50, 'C'),
                     (40, 'C-'),
                     (0, ' D'),
                     (-1, 'N/A'),
                     (-100, 'COPY'),
                     (-1000, 'FAIL'),
                     )
    score = models.IntegerField("本节成绩", choices=score_choices, default=-1)
    homework_note = models.CharField(verbose_name='作业评语', max_length=255, blank=True, null=True)
    note = models.CharField(verbose_name="备注", max_length=255, blank=True, null=True)

    homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None)
    stu_memo = models.TextField(verbose_name='学员备注', blank=True, null=True)
    date = models.DateTimeField(verbose_name='提交作业日期', auto_now_add=True)

    def __str__(self):
        return "{0}-{1}".format(self.classstudyrecord, self.student)

    class Meta:
        unique_together=["student","classstudyrecord"]

# 学生记录表是由上课记录表批量生成的

数据模型
数据模型
1
2
3
表结构分析:
1、学生学习记录表和学生表是多对一的关系;一个学生有多条学习记录
2、学生学习记录和班级记录是多对一的关系;一条班级记录是这个班所有学生的学习记录

2、实现功能

班级记录:

学生学习详细记录:

1
2
3
4
5
6
7
8
批量创建:
1、获取所有班级的班级记录展示到前端页面;
2、前端批量选择班级记录,提交到后端,前端提交时携带着每个班级记录的pk值;
3、后端通过前端提交过来的pk值连表找到每个班级的所有学生;
4、批量将每个班级的上课记录添加给这个班的每一个学生;
 
批量更新:
批量更新时需要注意,由于我们是批量修改的,所以前端在提交数据的时候必须标明我们修改的是哪个学生的学习记录,一般我们用这个学生的id来标注,这样后端在更新数据的时候就拿着这个学生的id去更新

自定义版本批量更新

# 批量将班级记录更新给这个班级的每个学生
class Class_study_records(View):
    # 展示上课记录表内容
    def get(self, request):
        class_obj = models.ClassStudyRecord.objects.all()
        return render(request, "class_study_records.html", {"class_obj": class_obj})

    # 批量将班级的上课记录添加给班级的所有学生
    def post(self, request):
        action = request.POST.get("action")
        selected_id = request.POST.getlist("selected_id")
        if hasattr(self, action):
            getattr(self, action)(selected_id)
        # 这样返回页面减少一次重定向
        return self.get(request)

    # 批量创建学生学习记录
    def batch_record(self, selected_id):
        # 循环每一个班级的上课记录
        for record in selected_id:
            # 通过班级记录找到班级的所有学生
            all_student = models.ClassStudyRecord.objects.get(pk=record).class_obj.students.all()
            li = []
            # 将班级记录添加给班级的所有学生
            for student in all_student:
                obj = models.StudentStudyRecord(
                    student=student,
                    classstudyrecord_id=record
                )
                li.append(obj)
            models.StudentStudyRecord.objects.bulk_create(li)

# 自定义版本
# 批量更新学生学习记录
class Student_study_records(View):
    def get(self, request, record_id):
        # 通过前端发过来的班级记录的id找到该班级
        class_obj = models.ClassStudyRecord.objects.get(pk=record_id)
        # 找到这个课程记录所生成的所有学生学习记录
        student_obj = models.StudentStudyRecord.objects.filter(
            classstudyrecord=class_obj)
        # 通过调用类中的属性找到所有可以选择的成绩
        score_choices = models.StudentStudyRecord.score_choices
        return render(request, "student_study_records1.html",
                      {"student_obj": student_obj, "class_obj": class_obj, "score_choices": score_choices})

    # 因为form表单是在当前页提交,url会带有一个id值,
    def post(self, request, record_id):
        student_dic = request.POST
        # print(student_dic)
        # <QueryDict: {'csrfmiddlewaretoken': ['igIDxQDr1xLFmyRGXpmtED6aE9AOtyQosGJq1WI5VNabN07O1aQuLzO6G9mWLNaK'], 'score_11': ['60'], 'homework_note_11': ['1'], 'score_12': ['0'], 'homework_note_12': ['2']}>
        """
            {
                1:{'score':85,'homework_note':'333'},
                2:{'score':85,'homework_note':'333'}
            }
        """
        data_dict = {}
        for key, value in student_dic.items():
            if key == "csrfmiddlewaretoken":
                continue
            field, pk = key.rsplit("_", 1)
            # 往字典中的同一个key里,插入多条value,防止第二次添加的把第一条数据替换掉
            if pk in data_dict:
                data_dict[pk][field] = value
            else:
                data_dict[pk] = {
                    field: value,
                }
        for spk, sdata in data_dict.items():
            models.StudentStudyRecord.objects.filter(**{"pk": spk}).update(**sdata)
        return self.get(request, record_id)

逻辑 函数
逻辑函数
{% extends "template.html" %}
{% block countent %}

    <section class="content">
        <div class="row">
            <div class="col-xs-12">
                <div class="box">

                    <form action="" method="post">
                        {% csrf_token %}
                        <select name="action" id="" class="form-control" style="width: 200px;display: inline-block;margin-top: 10px;margin-left: 9px">
                            <option value="batch_record">批量更新学生学习记录</option>

                        </select>
                        <input type="submit" value="go" class="btn btn-danger" style="vertical-align: 1px">

                        <!-- /.box-header -->
                        <div class="box-body">
                            <table id="example2" class="table table-bordered table-hover">
                                <thead>
                                <tr>
                                    <th>
                                        <input type="checkbox" id="choice">
                                    </th>
                                    <th>序号</th>
                                    <th>班级</th>
                                    <th>节次</th>
                                    <th>讲师</th>
                                    <th>课程标题</th>
                                    <th>学生学习详细记录</th>
                                    <th>操作</th>
                                </tr>
                                </thead>
                                <tbody>
                                {% for record in class_obj %}
                                    <tr>
                                        <td>
                                            <input type="checkbox" name="selected_id" value="{{ record.pk }}">
                                        </td>
                                        <td>{{ forloop.counter }}</td>
                                        <td>{{ record.class_obj }}</td>
                                        <td>{{ record.day_num }}</td>
                                        <td>{{ record.teacher }}</td>
                                        <td>{{ record.course_title }}</td>
                                        <td><a href="{% url "student_study_records" record.pk %}">详细记录</a></td>
                                        <td>
                                            <a href=""
                                               class="btn btn-primary btn-sm">编辑</a>
                                            <a href=""
                                               class="btn btn-danger btn-sm">删除</a>
                                        </td>
                                    </tr>
                                {% endfor %}

                                </tbody>
                            </table>
                        </div>
                    </form>
                    <!-- /.box-body -->
                </div>
                <!-- /.box -->

                <!-- /.box -->
            </div>
            <!-- /.col -->
        </div>
        <!-- /.row -->
    </section>
{% endblock %}
{% block js %}
    $("#choice").click(function () {
    // 获取当前选中的状态true或false
    var state = $(this).prop("checked");
    $("[name=selected_id]").prop("checked",state)
    })
{% endblock %}

班级记录页面展示
班级记录页面展示
{% extends "template.html" %}
{% block countent %}
    <h3>班级:{{ class_obj.class_obj }}第{{ class_obj.day_num }}天</h3>
    <div class="container-fluid">
        <form action="" method="post">
        {% csrf_token %}
        <input type="submit" value="提交">
            <table class="table table-hover table-bordered ">
                <thead>
                <tr>
                    <th>序号</th>
                    <th>学员</th>
                    <th>第几天课程</th>
                    <th>上课记录</th>
                    <th>本节成绩</th>
                    <th>批语</th>
                </tr>
                </thead>
                <tbody>
                {% for student in student_obj %}
                    <tr>
                        <td>{{ forloop.counter }}</td>
                        <td>{{ student.student }}</td>
                        <td>{{ student.classstudyrecord }}</td>
                        <td>
                            {{ student.get_record_display }}
                        </td>
                        <td>
                            <select name="score_{{ student.pk }}" id="">
                                {% for choices in score_choices %}
                                    {% if student.score == choices.0 %}
                                        <option selected value="{{ choices.0 }}">{{ choices.1 }}</option>
                                    {% else %}
                                        <option value="{{ choices.0 }}">{{ choices.1 }}</option>
                                    {% endif %}

                                {% endfor %}


                            </select>
                        </td>
                        <td>
                        <textarea name="homework_note_{{ student.pk }}" id="" cols="20" rows="1">{{ student.homework_note|default:'' }}</textarea>

                        </td>
                    </tr>
                {% endfor %}

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

{% endblock %}

学生学习记录页面展示
学生学习记录页面展示

modelformset_factory

from django.forms.models import modelformset_factory
from django import forms

class StudyRecordDeialModelForm(forms.ModelForm):
    class Meta:
        model = models.StudentStudyRecord  # 指定表
        # fields = "__all__"
        # 指定验证字段
        fields = ["score","homework_note"]

# modelformset_factory
class Student_study_records(View):
    def get(self, request, record_id):
        # 通过前端发过来的班级记录的id找到该班级
        class_obj = models.ClassStudyRecord.objects.get(pk=record_id)
        # 获取这个班级的所有学生学习记录表
        student_obj = models.StudentStudyRecord.objects.filter(classstudyrecord=class_obj)
        # 用StudentStudyRecord张表指定哪个modelform,对哪些数据进行加工
        form_set_obj = modelformset_factory(model=models.StudentStudyRecord,form=StudyRecordDeialModelForm,extra=0)
        # 指定加工表里的哪些数据
        formset = form_set_obj(queryset=student_obj)
        return render(request, "student_study_records1.html",{"formset":formset})

    # form表单是在当前页提交,url会带有一个id值,
    def post(self, request, record_id):
        # 通过前端发过来的班级记录的id找到该班级
        class_obj = models.ClassStudyRecord.objects.get(pk=record_id)
        # 获取这个班级的所有学生学习记录表
        student_obj = models.StudentStudyRecord.objects.filter(classstudyrecord=class_obj)
        # 用StudentStudyRecord张表指定哪个modelform,对哪些数据进行加工
        form_set_obj = modelformset_factory(model=models.StudentStudyRecord, form=StudyRecordDeialModelForm, extra=0)

        # 将前端提交过来的数据交给这个对象去处理
        formset = form_set_obj(request.POST)
        # modelform对数据进行验证
        if formset.is_valid():
            # 验证成功保存到数据库
            formset.save()
        else:
            # 打印错误信息
            print(formset.errors)
        return self.get(request, record_id)

逻辑函数
逻辑函数
{% extends "template.html" %}
{% block countent %}
    <h3>班级:{{ class_obj.class_obj }}第{{ class_obj.day_num }}天</h3>
    <div class="panel panel-default">
        <div class="panel-heading">学习记录</div>
        <div class="panel-body">
            <div style="width: 680px;margin: 0 auto;">
                <form method="post" action="">
                    {% 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 }} <!--必须用id不能用pk,用来标识是修改的哪条数据-->
                                <td>{{ form.instance.student }}</td>
                                <!--默认样式是下拉选择框,指定instance显示原数据的值-->
{#                                <td>{{ form.instance.student }}</td>#}
                                <td>{{ form.instance.get_record_display }} </td>
{#                                <td>{{ form.instance.get_record_display }} </td>#}
                                <td>{{ form.score }} </td>
                                <td>{{ form.homework_note }}</td>
                            </tr>
                        {% endfor %}
                        </tbody>
                    </table>
                    <input type="submit" value="保存">
                </form>
            </div>
        </div>
    </div>

{% endblock %}

学生学习记录页面展示
学生学习记录页面展示
1
2
3
4
5
6
注意前端页面渲染时必须加上,固定写法
{{ formset.management_form }}
 
我们在前端提交数据的时候必须用id来标识我们修改的是哪条数据,用于后端进行识别后保存进数据库,
该字段在前端页面是不显示的;
注意:不能用pk做标识符,pk默认是从0开始的,不能用pk,不能用pk,不能用pk,重要的事情说三遍!!!

推荐阅读