Python入门自学进阶-Web框架——32上课作业流程开发

Posted kaoa000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python入门自学进阶-Web框架——32上课作业流程开发相关的知识,希望对你有一定的参考价值。

对学员上课、作业进行管理。首先对几个表,即model用途进行阐述:

课程表存放要开什么课,有了课程表,就要有班级,如开了Python入门课程,再根据学员人数,组成班级,如Python入门课程一班、二班等,有了班级就可以开课了,上课老师需要在上课记录表中记录给哪个班级上了第几天课,老师上课需要点名,在学习记录表中对学生点名。

这里主要是学习记录的操作,老师点名时,不能一个学生一个学生的添加,最好就是批量生成所有学生的记录,默认都是已签到,老师点名时不需要在添加学生,只需修改签到状态,及课后成绩修改等。

学习记录:

使用Action来批量生成。

在Django的admin.py中,做如下配置(基本框架的形成):

class CourseRecordAdmin(admin.ModelAdmin):
    list_display = ['from_class','day_num','teacher','has_homework','homework_title']
    def initialize_studyrecords(self,req,queryset):
        print('init studyrecords')
    initialize_studyrecords.short_description = "批量初始化"   # 在action中显示的名字
    actions = ['initialize_studyrecords',]

admin.site.register(models.CourseRecord,CourseRecordAdmin)

配置后的结果:

此时,选中一条上课记录,在Action中选择批量初始化,就可以在学习记录表中生成这节课的所有学员的学习记录。框架出来后,就要对具体的函数,即initialize_studyrecords实现功能。

首先要查询出有哪些学员关联了这节课。查询这个班的学员就可以了。同时,批量生成时,只能选择一节课,因为一个讲师不能同时上多节课,在函数中要先判断是否只选择了一条记录。

Action函数initialize_studyrecords的具体实现:

    def initialize_studyrecords(self,req,queryset):
        if len(queryset)>1:
            return HttpResponse("只能选择一节课 ")
        print(queryset[0].from_class.enrollment_set.all())  # 先打印一下学员明细
        # for enroll_obj in queryset[0].from_class.enrollment_set.all():  # 下面是一个普通实现,一条记录一条记录的插入,效率低
            # # models.StudyRecord.objects.create(  # 使用create时,如果在批量生成前,做了一条学习记录,再批量创建时会报"Duplicate entry“错误,不唯一了
            # models.StudyRecord.objects.get_or_create( # 使用get_or_create,可以避免唯一错误
            #     student= enroll_obj,
            #     course_record= queryset[0],
            #     attendance= 0,
            #     score= 0,
            # )
            # ====上面一条一条插入效率不高,使用下面的事务批量插入,即使用bulk_create
        studyrecord_obj_list = []
        for enroll_obj in queryset[0].from_class.enrollment_set.all():
            studyrecord_obj_list.append(models.StudyRecord(
                student=enroll_obj,
                course_record=queryset[0],
                attendance=0,
                score=0,
            ))
        try:
            models.StudyRecord.objects.bulk_create(studyrecord_obj_list)
        except Exception as e:
            return HttpResponse("批量创建失败,请检查是否有手动创建学习记录或已经批量生成")
        return redirect("/admin/plcrm/studyrecord/?course_record__id__exact=%s" % queryset[0].id) # 带参数,可以显示批量创建的这节课的相关学习记录,否则显示所有的学习记录
    

上面是在Django的admin中实现的批量处理,现在将此功能添加到我们自己的admin中,即mytestapp_admin中

class CourseRecordAdmin(BaseAdmin):
    list_display = ['from_class','day_num','teacher','has_homework','homework_title']
    def initialize_studyrecords(self,req,queryset):
        if len(queryset)>1:
            print("leng >1")
            return HttpResponse("只能选择一节课 ")
        # for enroll_obj in queryset[0].from_class.enrollment_set.all():
            # # models.StudyRecord.objects.create(  # 使用create时,如果在批量生成前,做了一条学习记录,再批量创建时会报"Duplicate entry“错误,不唯一了
            # models.StudyRecord.objects.get_or_create( # 使用get_or_create,可以避免唯一错误
            #     student= enroll_obj,
            #     course_record= queryset[0],
            #     attendance= 0,
            #     score= 0,
            # )
            # ====上面一条一条插入效率不高,使用下面的事务批量插入,即使用bulk_create
        studyrecord_obj_list = []
        for enroll_obj in queryset[0].from_class.enrollment_set.all():
            studyrecord_obj_list.append(models.StudyRecord(
                student=enroll_obj,
                course_record=queryset[0],
                attendance=0,
                score=0,
            ))
        try:
            models.StudyRecord.objects.bulk_create(studyrecord_obj_list)
        except Exception as e:
            return HttpResponse("批量创建失败,请检查是否有手动创建学习记录或已经批量生成")
        return redirect("/mytestapp/plcrm/studyrecord/?course_record=%s" % queryset[0].id) # 带参数,可以显示批量创建的这节课的相关学习记录,否则显示所有的学习记录
    initialize_studyrecords.display_name = "批量初始化"
    actions = ['initialize_studyrecords',]

在具体调用action函数的程序中(views函数),调用的片段:

    if req.method == "POST":
        if not admin_class.readonly_table:
            select_ids = req.POST.get('selected_ids')
            select_ids = select_ids.split(',')
            actions = req.POST.get('myaction')
            action_obj = getattr(admin_class,actions)
            models_objs = admin_class.model.objects.filter(id__in=select_ids)

            # action_obj(admin_class,req,models_objs)  # 执行action函数,原来程序到这里就结束了

            return_httpresponse_obj = action_obj(admin_class, req, models_objs)
            print("======>>>",return_httpresponse_obj)
            return return_httpresponse_obj
            # 在编写action实现批量生成学习记录时,修改为上面两句,因为
        else:
            error = 'readonlyerror:Table is readonly, cannot be deleted!'

这里有一个很重要的知识点:就是作为views函数,即在路由项中定义的对应函数,必须返回一个HttpResponse,否则就会出错;在views函数中调用其他的函数,其他函数含有return HttpResponse(),只是返回一个HttpResponse对象,不会真正的返回给前端,只有views函数的return才能返回前端。我的视图函数中在处理action时,一开始只是调用了action函数,即只执行了action_obj(admin_class,req,models_objs)这一句,我以为action函数,这里指initialize_studyrecords中最后执行的是return HttpResponse(“失败”),views函数也就返回了响应,实际上不会,action_obj(admin_class,req,models_objs)这一句只是获得了一个HttpResponse对象,并且没有用变量保存,也就是直接丢弃了,然后views函数会接着往下运行,然后在前端返回的是views函数最后返回的return页。所以要捕获action函数返回的值,即HttpResponse或HttpResponseRedirect对象,在return

这样,我们的自定义app就包含了批量初始化学习记录的功能。

学员课程流程开发:主要是学生账号设置和学生课程管理,主要是作业管理,提交作业。

在客户报名成功成为学员后,给学员创建一个账号,并关联上客户信息,在学员登录后,根据相应的客户信息,找到学员所报课程相关信息,并给出课程进度、课程成绩等信息,然后再给出一个链接,对所报课程进行作业管理。

修改账户表,增加一个客户信息的关联字段,然后创建账号:

学员角色以及角色对应的菜单项:

学员登录后的主页:

点击学生课程菜单,进入课程管理页:

在项目的urls中增加学员登录的路由项以及学员管理应用的urls

path('',views.index,name="stu_index"),

path('student/',include("student.urls")),

学员课程菜单对应的实现:

student.urls中的路由项:path('student_class/',views.student_class,name="student_class"),

视图函数student_class:

def student_class(req):

    return  render(req,'student/student_class.html')

前端页面student_class.html:

% extends "base.html" %
% load stu_tags %
% block mybody %
<body>
    
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
..页头部分,略
</nav>

<div class="container-fluid">
  <div class="row">
    <nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
      <div class="sidebar-sticky pt-3">
      <div style="height: 40px;background-color: black"></div>
        <ul class="nav flex-column">
          % for role in request.user.roles.select_related %
            <!--    role     测试role的值,这里应该是roles类的__str__()方法的值 -->
            % for menu in role.menus.all %
                <li class="nav-item">
                <a class="nav-link active" href="%  url menu.url_name %">
              <span data-feather="home"></span>
                     menu.name  <span class="sr-only">(current)</span>
            </a>
          </li>
            % endfor %
          % endfor %
        </ul>
      </div>
    </nav>

    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
      <div class="card">
          <div class="card-header h4">
            学生 request.user.name 课程信息
          </div>
          <div class="card-body">
            <table class="table table-striped table-responsive">
                <thead>
                    <tr>
                        <th>课程名称</th>
                        <th>班级</th>
                        <th>开课日期</th>
                        <th>结业日期</th>
                        <th>课程进度</th>
                        <th>我的成绩</th>
                        <th>作业管理</th>
                    </tr>
                </thead>
                <tbody>
                    % for enroll_obj in request.user.stu_account.enrollment_set.all %
                    <tr>
                        <td> enroll_obj.enrolled_class.course </td>
                        <td>s enroll_obj.enrolled_class.semester </td>
                        <td> enroll_obj.enrolled_class.start_date </td>
                        <td> enroll_obj.enrolled_class.end_date </td>
                        <td>已上 enroll_obj.enrolled_class.courserecord_set.all.count 节</td>
                        <td>% get_score enroll_obj request.user.stu_account as score_date % score_date.score__sum </td>
                        <td><a href="#">作业管理</a></td>
                    </tr>
                    % endfor %
                </tbody>
            </table>
          </div>
        </div>
    </main>
  </div>
</div>
。。。
  </body>
% endblock %

学员的信息,通过账号关联的客户信息,在通过关联的报名信息、班级信息、上课信息等,可以正查或反查出各种信息,再通过聚合函数统计等得出各种信息。

学员成绩查询时使用了自定义标签get_score enroll_obj:

@register.simple_tag
def get_score(enroll_obj,customer_obj):
    study_records = enroll_obj.studyrecord_set.\\
        filter(course_record__from_class_id=enroll_obj.enrolled_class.id)
    for record  in study_records:
        print(record)
    return study_records.aggregate(Sum("score"))

作业管理,点击作业管理后,进入显示某一班级的所有上课记录的页面:

显示此学员此班级的上课记录信息,在这里也有个作业管理,用于学员上传作业,此页面:

这里作业提交管理使用dropzone插件,完成作业文件上传,文件要使用zip格式。

某一班级学习管理:path('studyrecords/<int:enroll_obj_id>/',views.studyrecords,name='studyrecords'),

视图函数studyrecords:

def studyrecords(req,enroll_obj_id):
    enroll_obj =models.Enrollment.objects.get(id=enroll_obj_id)

    return render(req,'student/studyrecords.html','enroll_obj':enroll_obj,)

前端页面/studyrecords.html:

<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
      <div class="card">
          <div class="card-header h4">
            学生: request.user.name  -->  enroll_obj.enrolled_class.course  学习信息
          </div>
          <div class="card-body">
            <table class="table table-striped table-responsive">
                <thead>
                    <tr>
                        <th>课程节次</th>
                        <th>上课日期</th>
                        <th>是否有作业</th>
                        <th>作业标题</th>
                        <th>签到状态</th>
                        <th>成绩</th>
                        <th>作业管理</th>
                    </tr>
                </thead>
                <tbody>
                    % for sturecord in enroll_obj.studyrecord_set.all %
                    <tr>
                        <td> sturecord.course_record.day_num </td>
                        <td> sturecord.course_record.date </td>
                        <td> sturecord.course_record.has_homework </td>
                        <td> sturecord.course_record.homework_title </td>
                        <td> sturecord.get_attendance_display </td>
                        <td> sturecord.get_score_display </td>
                        <td><a href="% url 'homework_detail' sturecord.id %">作业管理</a></td>
                    </tr>
                    % endfor %
                </tbody>
            </table>
          </div>
        </div>
    </main>

具体谋一节课的作业管理:

path('homework_detail/<int:sturecord_id>/',views.homework_detail,name='homework_detail'),

视图函数homework_detail:

def homework_detail(req,sturecord_id):
    sturecord_obj = models.StudyRecord.objects.get(id=sturecord_id)
    if req.method == "POST":
    # class_id/course_record_id/studyrecord_id
        homework_path = "base_dir/class_id/course_record_id/studyrecord_id/".format(
            base_dir = settings.HOMEWORK_DATA,
            class_id = sturecord_obj.student.enrolled_class_id,
            course_record_id = sturecord_obj.course_record_id,
            studyrecord_id = sturecord_obj.id,
        )
        if not os.path.isdir(homework_path):
            os.makedirs(homework_path,exist_ok=True)

        for k,file_obj in req.FILES.items():
            with open("%s/%s" %(homework_path,file_obj.name),"wb") as f:
                for chunk in file_obj.chunks():
                    f.write(chunk)
        return HttpResponse(json.dumps("status":0,"msg":"file upload success"))
    return render(req,"student/homework_detail.html",'sturecord_obj':sturecord_obj,)

前端页面homework_detail.html:

<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
      <div class="card">
          <div class="card-header h4">
             sturecord_obj.course_record.from_class.course  第 sturecord_obj.course_record.day_num 节课
          </div>
          <div class="card-body">
            <h5>作业标题: sturecord_obj.course_record.homework_title </h5>
            <h5>作业详情:</h5><pre><P class="bg-light col-lg-10 border pt-2 pb-2"> sturecord_obj.course_record.homework_content </P></pre>
            <h5>老师评语:</h5><P class="bg-light col-lg-10 border pt-2 pb-2"> sturecord_obj.memo </P>
            <h5>本节成绩: sturecord_obj.score </h5>
            <hr/>
            <h5>作业提交管理:</h5>
            <div class="col-lg-10">
              <form id="mydropz" action= request.path  class="dropzone">
              <div class="fallback">
                <input name="file" type="file" multiple />
              </div>
            </form>
            </div>
          </div>
        </div>
    </main>

  </div>
</div>

    <script>
        if ($("form#mydropz").length !=0)  

        Dropzone.options.mydropz = false;
        var myDropzone = new Dropzone("form#mydropz",
            headers:'X-CSRFToken':'csrf_token',
            dictDefaultMessage:"将打包的作业拖至此框",
            maxFiles: 1,
            accept:function (file,done) 
                if( !file.name.endsWith(".zip"))
                    alert("只能传zip文件");
                    done("文件未上传");
                    myDropzone.reset();

                  else done();
            
    );
    
    </script>

这里使用了dropzone插件,下面的js脚本用于dropzone的初始化和处理。

以上是关于Python入门自学进阶-Web框架——32上课作业流程开发的主要内容,如果未能解决你的问题,请参考以下文章

Python入门自学进阶-Web框架——12Django实践小项目2

Python入门自学进阶-Web框架——18FormModelForm

Python入门自学进阶-Web框架——18FormModelForm

Python入门自学进阶-Web框架——20Django其他相关知识2

Python入门自学进阶-Web框架——2Django初识

Python入门自学进阶-Web框架——3Django的URL配置