Django 小實例S1 簡易學生選課管理系統 第11節——學生課程業務實現
點擊查看教程總目錄
作者自我介紹:b站小UP主,時常直播編程+紅警三,python1對1輔導老師。
課程模塊中,學生需要擁有的功能有:
- 查看課程列表
- 選課撤課
- 結課后評教
1 - 查看課程列表
學生可以按類別view_kind
查看課程,view_kind
分為
current
: 查看當前課程is_end
: 查看結課課程select
: 可選課的withdraw
: 可撤課的
新建學生查看課程的模板templates/course/student/home.html
如下
{% extends "course/nav.html" %}
{% block title %}HomePage{% endblock %}
{% block content %}
<div class="main-container">
<div class="main-bar">
<form class="search-form" method="post">
{% csrf_token %}
<input class="input" id="search-key" type="text" name="search" {% if search_key != None %}value="{{ search_key }}" {% endif %}/>
<input class="button" type="submit" value="搜索課程" />
</form>
{% if view_kind != "select"%}
<input class="button right-button" type="button" value="選課" onclick='window.open("{% url 'view_course' 'select' %}")' />
{% endif %}
{% if view_kind != "withdraw"%}
<input class="button right-button" type="button" value="撤課" onclick='window.open("{% url 'view_course' 'withdraw' %}")' />
{% endif %}
{% if view_kind != "is_end"%}
<input class="button right-button" type="button" value="查看結課課程" onclick='window.open("{% url 'view_course' 'is_end' %}")' />
{% endif %}
{% if view_kind != "current"%}
<input class="button right-button" type="button" value="查看當前課程" onclick='window.open("{% url 'view_course' 'current' %}")' />
{% endif %}
</div>
<h3>{% if view_kind == "select"%}
選課
{% elif view_kind == "withdraw"%}
撤課
{% elif view_kind == "is_end"%}
查看結課課程
{% elif view_kind == "current"%}
查看當前課程
{% endif %}</h3>
<table class="item-list course-list">
<thead>
<tr>
<th class="course-no">課程編號</th>
<th class="course-name">名稱</th>
<th class="course-credit">學分</th>
{% if view_kind == "is_end" %}
<th class="course-year-semester">年份學期</th>
{% else %}
<th class="course-number">當前人數/<br>最大人數</th>
<th class="course-year">年份</th>
<th class="course-semester">學期</th>
{% endif %}
<th class="course-teacher">教師</th>
{% if view_kind == "is_end" %}
<th class="course-scores">得分</th>
<th class="course-comments">評語</th>
<th class="course-rating">學生評分</th>
<th class="course-assessment">學生評價</th>
<th class="course-operation student-course">操作</th>
{% else %}
<th class="course-schedule">上課時間</th>
<th class="course-operation student-course">操作</th>
{% endif %}
</tr>
</thead>
<tbody>
{% if view_kind == "is_end" %}
{# end course show student course #}
{% for sc in course_list %}
<tr id="course-id-{{ sc.course.id }}">
<td class="course-no">{{ sc.course.id }}</td>
<td class="course-name">{{ sc.course.name }}</td>
<td class="course-credit">{{ sc.course.credit }}</td>
<td class="course-year-semester">{{ sc.course.year }} {{ sc.course.get_semester_display }}</td>
<td class="course-teacher">{{ sc.course.teacher.name }}</td>
<td class="course-scores">{{ sc.scores }}</td>
<td class="course-comments">{{ sc.comments }}</td>
{% if sc.rating == None %}
<td class="course-rating">-</td>
<td class="course-assessment">-</td>
<td class="course-operation student-course">
<input class="button" type="button" value="查看詳情" onclick='location.href="{% url 'sview_detail' sc.id%}"' />
<input class="button" type="button" value="評教"
onclick='window.open("{% url 'evaluate' sc.id %}")' />
</td>
{% else %}
<td class="course-rating">{{ sc.rating }}</td>
<td class="course-assessment">{{ sc.assessment }}</td>
<td class="course-operation student-course">
<input class="button" type="button" value="查看詳情" onclick='location.href="{% url 'sview_detail' sc.id%}"' />
</td>
{% endif %}
</tr>
{% endfor %}
{% else %}
{% for course in course_list %}
<tr id="course-id-{{ course.id }}">
<td class="course-no">{{ course.id }}</td>
<td class="course-name">{{ course.name }}</td>
<td class="course-credit">{{ course.credit }}</td>
<td class="course-number">{{ course.get_current_count }}/{{ course.max_number }}</td>
<td class="course-year">{{ course.year }}</td>
<td class="course-semester">{{ course.get_semester_display }}</td>
<td class="course-teacher">{{ course.teacher.name }}</td>
<td class="course-schedule">
{% for schedule in course.get_schedules %}
<div>{{ schedule }}</div>
{% endfor %}
</td>
<td class="course-operation student-course">
{% if view_kind == "select"%}
<input class="button" type="button" value="選課"
onclick='window.open("{% url 'operate_course' course.id 'select' %}")' />
{% endif %}
{% if view_kind == "withdraw"%}
<input class="button" type="button" value="撤課"
onclick='window.open("{% url 'operate_course' course.id 'withdraw' %}")' />
{% endif %}
{% if view_kind == "current"%}
-
{% endif %}
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
{% endblock %}
然后在course/views.py
中添加代碼如下
def view_course(request, view_kind):
"""
:param view_kind:
current: 查看當前課程
is_end: 查看結課課程
select: 選課
withdraw: 撤課
"""
user = get_user(request, "student")
if not user:
return redirect(reverse("login", kwargs={"kind": "student"}))
is_search = False
search_key = ""
if request.method == "POST":
search_key = request.POST.get("search")
if search_key:
is_search = True
info = {
"name": user.name,
"kind": "student",
}
course_list = []
if view_kind in ["select", "current", "withdraw", "is_end"]:
if view_kind == "select":
q = Q(status=2)
if is_search:
q = q & (Q(name__icontains=search_key) | Q(teacher__name__icontains=search_key))
course_list = Course.objects.filter(q)
my_course = StudentCourse.objects.filter(Q(student=user) & Q(with_draw=False))
my_cids = [c.course.id for c in my_course]
course_list = [c for c in course_list if c.id not in my_cids]
else:
q = Q(student=user) & Q(with_draw=False)
if is_search:
q = q & (Q(name__icontains=search_key) | Q(teacher__name__icontains=search_key))
my_course = StudentCourse.objects.filter(q)
if view_kind == "current":
course_list = [c.course for c in my_course if c.course.status < 4]
elif view_kind == "withdraw":
course_list = [c.course for c in my_course if c.course.status == 2]
elif view_kind == "is_end":
course_list = [c for c in my_course if c.course.status >= 4]
else:
return HttpResponse(INVALID_REQUEST_METHOD)
context = {
'info': info,
'view_kind': view_kind,
'course_list': course_list
}
if is_search:
context["search_key"] = search_key
return render(request, 'course/student/home.html', context)
課程主頁即學生的個人主頁,故修改course/views.py
中的原視圖方法student_home
為
def student_home(request):
return redirect(reverse("view_course", kwargs={"view_kind": "current"}))
2 - 選課撤課
選課是新建一個學生課程關系記錄,撤課則是修改對應的學生課程關系記錄。
即學生有兩種操作課程方法,operate_kind
如下:
select
: 選課withdraw
: 撤課
如果網頁請求發送的方法不在這兩種里面,則不符合規范,同時需要將這一信息通過響應返回告知瀏覽器。
故在constants.py
中添加ILLEGAL_KIND = "Illegal kind for you."
在course/views.py
中,導入ILLEGAL_KIND
,然后添加代碼如下
# 在開頭導入timezone
from django.utils import timezone
def operate_course(request, operate_kind, course_id):
"""
:param operate_kind:
select: 選課
withdraw: 撤課
"""
user = get_user(request, "student")
if not user:
return redirect(reverse("login", kwargs={"kind": "student"}))
if operate_kind not in ["select", "withdraw"]:
return HttpResponse(ILLEGAL_KIND)
elif operate_kind == "select":
course = Course.objects.filter(pk=course_id).get()
new_course = StudentCourse(student=user, course=course)
new_course.save()
elif operate_kind == "withdraw":
q = Q(course__id=course_id) & Q(student=user) & Q(with_draw=False)
course = StudentCourse.objects.filter(q).get()
course.with_draw = True
course.with_draw_time = timezone.now()
course.save()
return redirect(reverse("view_course", kwargs={"view_kind": operate_kind}))
3 - 結課后評教
學生給老師評教和老師給學生評分的后端邏輯是一樣的,都是修改學生課程關系表內的數據。
先在course/forms.py
中添加
class RateForm(forms.ModelForm):
class Meta:
model = StudentCourse
fields = ["course", "scores", "comments", "rating", "assessment"]
course = forms.CharField(label="課程", disabled=True)
scores = forms.IntegerField(label="成績", disabled=True)
comments = forms.CharField(label="老師評價", disabled=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.initial['course'] = self.instance.course
self.initial['scores'] = self.instance.scores
self.initial['comments'] = self.instance.comments
def clean_course(self):
return self.initial['course']
def clean_scores(self):
return self.initial['scores']
def clean_comments(self):
return self.initial['comments']
然后添加模板文件templates/course/student/rating.html
:
{% extends "course/nav.html" %}
{% block title %}評教{% endblock %}
{% block content %}
<h3>評教</h3>
<div class="form create-update-from">
<form method="post">
{% csrf_token %}
{{form.as_p}}
<div class="submit-button">
<input type="submit" value="確定"/>
<input type="button" value="返回" onclick='location.href="{{ return_url }}"' />
</div>
</form>
</div>
{% endblock %}
再在course/cbvs.py
中導入RateForm
類,然后添加代碼如下
class RateUpdateView(UpdateView):
model = StudentCourse
form_class = RateForm
template_name = 'course/student/rating.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object()
info = {}
return_url = reverse("view_course", kwargs={"view_kind": "is_end"})
if self.object:
student = self.object.student
info = {
"name": student.name,
"kind": "student",
}
return self.render_to_response(self.get_context_data(info=info, return_url=return_url))
def get_success_url(self):
return reverse("view_course", kwargs={"view_kind": "is_end"})
4 - 學生課程詳情頁
這個使用CBVs實現起來最快
先添加模板templates/course/student/course.html
如下
{% extends "course/nav.html" %}
{% block title %}課程詳情{% endblock %}
{% block content %}
<h3>課程詳情</h3>
<ul class="course-details">
<li class="course-detail"><span class="detail-name">課程編號</span> {{ object.course.id }}</li>
<li class="course-detail"><span class="detail-name">課程名</span> {{ object.course.name }}</li>
<li class="course-detail"><span class="detail-name">學分</span> {{ object.course.credit }}</li>
<li class="course-detail"><span class="detail-name">課程人數/最大人數</span> {{ object.course.get_current_count }}/{{ object.course.max_number }}</li>
<li class="course-detail"><span class="detail-name">年份</span> {{ object.course.year }}</li>
<li class="course-detail"><span class="detail-name">學期</span> {{ object.course.get_semester_display }}</li>
<li class="course-detail"><span class="detail-name">教師</span> {{ object.course.teacher.name }}</li>
<li class="course-detail"><span class="detail-name">上課時間</span>
<span class="course-schedules">
{% for schedule in object.course.get_schedules %}
<div class="course-schedule">{{ schedule }}</div>
<div class="course-schedule">{{ schedule }}</div>
{% endfor %}
</span>
</li>
<li class="course-detail"><span class="detail-name">得分</span>
{% if object.scores != None %}{{ object.scores }}{% else %} - {% endif %}
</li>
<li class="course-detail"><span class="detail-name">評語</span>
{% if object.comments != None %}{{ object.comments }}{% else %} - {% endif %}
</li>
<li class="course-detail"><span class="detail-name">學生評分</span>
{% if object.rating != None %}{{ object.rating }}{% else %} - {% endif %}
</li>
<li class="course-detail"><span class="detail-name">學生評價</span>
{% if object.assessment != None %}{{ object.assessment }}{% else %} - {% endif %}
</li>
</ul>
<input type="button" value="返回" onclick='location.href="{% url 'view_course' 'is_end'%}"' />
{% endblock %}
再在course/cbvs.py
中添加代碼如下
class StudentCourseDetailView(DetailView):
model = StudentCourse
template_name = 'course/student/course.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
if self.object:
context["info"] = {
"name": self.object.student.name,
"kind": "student",
}
return self.render_to_response(context)
5 - 添加路由
上面已經把學生需要的視圖方法全部實現完畢了,接下來就是添加到路由里面。
修改后的course/urls.py
如下
from django.urls import path
from course.views import *
from course.cbvs import ScoreUpdateView, RateUpdateView, StudentCourseDetailView
urlpatterns = [
path('<slug:kind>/', home, name="course"),
path('teacher/create_course', create_course, name="create_course"),
path('teacher/view_detail/<int:course_id>', view_detail, name="view_detail"),
path('teacher/create_schedule/<int:course_id>', create_schedule, name="create_schedule"),
path('teacher/delete_schedule/<int:schedule_id>', delete_schedule, name="delete_schedule"),
path('teacher/score/<int:pk>', ScoreUpdateView.as_view(), name="score"),
path('teacher/handle_course/<int:course_id>/<int:handle_kind>', handle_course, name="handle_course"),
path('student/view/<slug:view_kind>', view_course, name="view_course"),
path('student/operate/<int:course_id>/<slug:operate_kind>', operate_course, name="operate_course"),
path('student/evaluate/<int:pk>', RateUpdateView.as_view(), name="evaluate"),
path('student/view_detail/<int:pk>', StudentCourseDetailView.as_view(), name="sview_detail"),
]
6 - 效果
選課頁面
當前課程頁面