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