Django 小实例S1 简易学生选课管理系统 11 学生课程业务实现


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 %}
    
{% csrf_token %}
{% if view_kind != "select"%} {% endif %} {% if view_kind != "withdraw"%} {% endif %} {% if view_kind != "is_end"%} {% endif %} {% if view_kind != "current"%} {% endif %}

{% if view_kind == "select"%} 选课 {% elif view_kind == "withdraw"%} 撤课 {% elif view_kind == "is_end"%} 查看结课课程 {% elif view_kind == "current"%} 查看当前课程 {% endif %}

{% if view_kind == "is_end" %} {% else %} {% endif %} {% if view_kind == "is_end" %} {% else %} {% endif %} {% if view_kind == "is_end" %} {# end course show student course #} {% for sc in course_list %} {% if sc.rating == None %} {% else %} {% endif %} {% endfor %} {% else %} {% for course in course_list %} {% endfor %} {% endif %}
课程编号 名称 学分年份学期当前人数/
最大人数
年份 学期教师得分 评语 学生评分 学生评价 操作上课时间 操作
{{ sc.course.id }} {{ sc.course.name }} {{ sc.course.credit }} {{ sc.course.year }} {{ sc.course.get_semester_display }} {{ sc.course.teacher.name }} {{ sc.scores }} {{ sc.comments }}- - {{ sc.rating }} {{ sc.assessment }}
{{ course.id }} {{ course.name }} {{ course.credit }} {{ course.get_current_count }}/{{ course.max_number }} {{ course.year }} {{ course.get_semester_display }} {{ course.teacher.name }} {% for schedule in course.get_schedules %}
{{ schedule }}
{% endfor %}
{% if view_kind == "select"%} {% endif %} {% if view_kind == "withdraw"%} {% endif %} {% if view_kind == "current"%} - {% endif %}
{% 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 %}
    

评教

{% csrf_token %} {{form.as_p}}
{% 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 %}
    

课程详情

  • 课程编号 {{ object.course.id }}
  • 课程名 {{ object.course.name }}
  • 学分 {{ object.course.credit }}
  • 课程人数/最大人数 {{ object.course.get_current_count }}/{{ object.course.max_number }}
  • 年份 {{ object.course.year }}
  • 学期 {{ object.course.get_semester_display }}
  • 教师 {{ object.course.teacher.name }}
  • 上课时间 {% for schedule in object.course.get_schedules %}
    {{ schedule }}
    {{ schedule }}
    {% endfor %}
  • 得分 {% if object.scores != None %}{{ object.scores }}{% else %} - {% endif %}
  • 评语 {% if object.comments != None %}{{ object.comments }}{% else %} - {% endif %}
  • 学生评分 {% if object.rating != None %}{{ object.rating }}{% else %} - {% endif %}
  • 学生评价 {% if object.assessment != None %}{{ object.assessment }}{% else %} - {% endif %}
{% 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('/', home, name="course"),
    path('teacher/create_course', create_course, name="create_course"),
    path('teacher/view_detail/', view_detail, name="view_detail"),
    path('teacher/create_schedule/', create_schedule, name="create_schedule"),
    path('teacher/delete_schedule/', delete_schedule, name="delete_schedule"),
    path('teacher/score/', ScoreUpdateView.as_view(), name="score"),
    path('teacher/handle_course//', handle_course, name="handle_course"),

    path('student/view/', view_course, name="view_course"),
    path('student/operate//', operate_course, name="operate_course"),

    path('student/evaluate/', RateUpdateView.as_view(), name="evaluate"),
    path('student/view_detail/', StudentCourseDetailView.as_view(), name="sview_detail"),
]

6 - 效果

选课页面

当前课程页面

相关