├── course ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0004_auto_20201021_1720.py │ ├── 0005_auto_20201021_2109.py │ ├── 0002_auto_20200813_1721.py │ ├── 0006_auto_20201104_1036.py │ ├── 0007_auto_20201104_1040.py │ ├── 0003_auto_20200917_1644.py │ └── 0001_initial.py ├── tests.py ├── admin.py ├── apps.py ├── urls.py ├── forms.py ├── cbvs.py ├── models.py └── views.py ├── scss ├── __init__.py ├── wsgi.py ├── urls.py └── settings.py ├── user ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── admin.py ├── tests.py ├── apps.py ├── urls.py ├── util.py ├── models.py ├── forms.py └── views.py ├── .gitignore ├── db.sqlite3 ├── docs ├── img │ ├── 0_1.png │ ├── 0_2.png │ ├── 0_3.png │ ├── 0_4.png │ ├── 1_1.png │ ├── 1_2.png │ ├── 2_1.png │ ├── 2_2.png │ ├── 2_3.png │ ├── 2_4.png │ ├── 2_5.png │ ├── 2_6.png │ ├── 4_1.png │ ├── 4_2.png │ ├── 4_3.png │ ├── 5_1.png │ ├── 5_2.png │ ├── 5_3.png │ ├── 6_1.png │ ├── 6_2.png │ ├── 7_1.png │ ├── 8_1.png │ ├── 8_2.png │ ├── 8_3.png │ ├── 8_4.png │ ├── 8_5.png │ ├── 10_1.png │ ├── 10_2.png │ ├── 10_3.png │ ├── 10_4.png │ ├── 11_1.png │ ├── 11_2.png │ ├── 12_1.png │ └── 12_2.png ├── auto_update │ ├── run.sh │ ├── all.json │ └── config.json ├── info │ └── cnblogs │ │ ├── blog │ │ └── start.md │ │ └── catalog │ │ └── start.md ├── 1.md ├── 0.md ├── git_info │ └── cnblogs.json ├── 3.md ├── 7.md ├── 9.md ├── 2.md ├── 4.md ├── 6.md ├── 12.md ├── 8.md ├── 5.md ├── 11.md └── 10.md ├── templates ├── user │ ├── login_home.html │ ├── register.html │ ├── update.html │ ├── background.html │ └── login_detail.html └── course │ ├── student │ ├── rating.html │ ├── course.html │ └── home.html │ ├── teacher │ ├── score.html │ ├── create_course.html │ ├── create_schedule.html │ ├── home.html │ └── course.html │ └── nav.html ├── small.py ├── constants.py ├── static └── css │ ├── form.css │ ├── details.css │ ├── course_main.css │ ├── refer.css │ ├── nav.css │ ├── list.css │ ├── register.css │ └── login.css ├── manage.py ├── TODO.md ├── README.md └── LICENSE /course/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scss/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /user/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | *.pyc -------------------------------------------------------------------------------- /user/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /course/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /course/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /user/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /user/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /course/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/db.sqlite3 -------------------------------------------------------------------------------- /docs/img/0_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/0_1.png -------------------------------------------------------------------------------- /docs/img/0_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/0_2.png -------------------------------------------------------------------------------- /docs/img/0_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/0_3.png -------------------------------------------------------------------------------- /docs/img/0_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/0_4.png -------------------------------------------------------------------------------- /docs/img/1_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/1_1.png -------------------------------------------------------------------------------- /docs/img/1_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/1_2.png -------------------------------------------------------------------------------- /docs/img/2_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/2_1.png -------------------------------------------------------------------------------- /docs/img/2_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/2_2.png -------------------------------------------------------------------------------- /docs/img/2_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/2_3.png -------------------------------------------------------------------------------- /docs/img/2_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/2_4.png -------------------------------------------------------------------------------- /docs/img/2_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/2_5.png -------------------------------------------------------------------------------- /docs/img/2_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/2_6.png -------------------------------------------------------------------------------- /docs/img/4_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/4_1.png -------------------------------------------------------------------------------- /docs/img/4_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/4_2.png -------------------------------------------------------------------------------- /docs/img/4_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/4_3.png -------------------------------------------------------------------------------- /docs/img/5_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/5_1.png -------------------------------------------------------------------------------- /docs/img/5_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/5_2.png -------------------------------------------------------------------------------- /docs/img/5_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/5_3.png -------------------------------------------------------------------------------- /docs/img/6_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/6_1.png -------------------------------------------------------------------------------- /docs/img/6_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/6_2.png -------------------------------------------------------------------------------- /docs/img/7_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/7_1.png -------------------------------------------------------------------------------- /docs/img/8_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/8_1.png -------------------------------------------------------------------------------- /docs/img/8_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/8_2.png -------------------------------------------------------------------------------- /docs/img/8_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/8_3.png -------------------------------------------------------------------------------- /docs/img/8_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/8_4.png -------------------------------------------------------------------------------- /docs/img/8_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/8_5.png -------------------------------------------------------------------------------- /docs/img/10_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/10_1.png -------------------------------------------------------------------------------- /docs/img/10_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/10_2.png -------------------------------------------------------------------------------- /docs/img/10_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/10_3.png -------------------------------------------------------------------------------- /docs/img/10_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/10_4.png -------------------------------------------------------------------------------- /docs/img/11_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/11_1.png -------------------------------------------------------------------------------- /docs/img/11_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/11_2.png -------------------------------------------------------------------------------- /docs/img/12_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/12_1.png -------------------------------------------------------------------------------- /docs/img/12_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/HEAD/docs/img/12_2.png -------------------------------------------------------------------------------- /user/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UserConfig(AppConfig): 5 | name = 'user' 6 | -------------------------------------------------------------------------------- /course/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CourseConfig(AppConfig): 5 | name = 'course' 6 | -------------------------------------------------------------------------------- /docs/auto_update/run.sh: -------------------------------------------------------------------------------- 1 | python "F:\UP PIG\blog\blogs_auto_updating\autohander.py" "F:\UP PIG\code\SSCMS\docs\auto_update\all.json" 2 | -------------------------------------------------------------------------------- /docs/auto_update/all.json: -------------------------------------------------------------------------------- 1 | { 2 | "git_info": "F://UP PIG//code//SSCMS//docs//git_info", 3 | "config": "F://UP PIG//code//SSCMS//docs//auto_update//config.json", 4 | "refresh_catalog": false 5 | } 6 | -------------------------------------------------------------------------------- /docs/info/cnblogs/blog/start.md: -------------------------------------------------------------------------------- 1 | > Django 小实例S1 简易学生选课管理系统 第%s节——%s 2 | > [点击查看教程总目录](https://www.cnblogs.com/BigShuang/p/14304389.html) 3 | > 作者自我介绍:[b站小UP主](https://space.bilibili.com/149259132),[时常直播编程+红警三](https://live.bilibili.com/13337214),[python1对1辅导老师](https://www.bilibili.com/read/cv8288962)。 -------------------------------------------------------------------------------- /templates/user/login_home.html: -------------------------------------------------------------------------------- 1 | {% extends "user/background.html" %} 2 | {% block login_container %} 3 |
4 | 学生登录 5 |
6 |
7 | 教师登录 8 |
9 | {% endblock %} -------------------------------------------------------------------------------- /user/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from user import views 3 | 4 | 5 | urlpatterns = [ 6 | path('login/', views.home, name="login"), 7 | path('login/', views.login, name="login"), 8 | path('register/', views.register, name="register"), 9 | 10 | path('update/', views.update, name="update"), 11 | path('logout/', views.logout, name="logout"), 12 | ] 13 | -------------------------------------------------------------------------------- /small.py: -------------------------------------------------------------------------------- 1 | #usr/bin/env python 2 | #-*- coding:utf-8- -*- 3 | 4 | 5 | def check_login(func): 6 | def check(*args, **kwargs): 7 | print(args) 8 | print(kwargs) 9 | return func(*args, **kwargs) 10 | 11 | return check 12 | 13 | 14 | @check_login 15 | def simple_login(request, kind): 16 | print("simple test") 17 | return request 18 | 19 | 20 | if __name__ == '__main__': 21 | simple_login("req", "student") -------------------------------------------------------------------------------- /scss/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for scss project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'scss.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /constants.py: -------------------------------------------------------------------------------- 1 | 2 | INVALID_KIND = "Invalid kind.kind should be student or teacher." 3 | ILLEGAL_KIND = "Illegal kind for you." 4 | INVALID_URL = "Invalid Url." 5 | 6 | INVALID_REQUEST_METHOD = "Invalid request method." 7 | 8 | COURSE_STATUS = { 9 | 1: "未开始选课", 10 | 2: "开始选课", 11 | 3: "结束选课", 12 | 4: "结课", 13 | 5: "打分完成", 14 | } 15 | 16 | COURSE_OPERATION = { 17 | 1: "开始选课", 18 | 2: "结束选课", 19 | 3: "结课", 20 | 4: "给分", 21 | 5: "查看详情" 22 | } -------------------------------------------------------------------------------- /docs/info/cnblogs/catalog/start.md: -------------------------------------------------------------------------------- 1 | > python Django实现的一个简易的教务选课系统。 2 | > 介绍与演示的视频版本已发到我的b站: [https://www.bilibili.com/video/BV1er4y1w7ty](https://www.bilibili.com/video/BV1er4y1w7ty)。 3 | > 项目已上传到我的github: [https://github.com/BigShuang/SimpleStudentCourseManagementSystem](https://github.com/BigShuang/SimpleStudentCourseManagementSystem)。 4 | > 作者自我介绍:[b站小UP主](https://space.bilibili.com/149259132),[时常直播编程+红警三](https://live.bilibili.com/13337214),[python1对1辅导老师](https://www.bilibili.com/read/cv8288962)。 -------------------------------------------------------------------------------- /docs/auto_update/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "common": { 3 | "project": "F:\\UP PIG\\code\\SSCMS", 4 | "contents": "docs", 5 | "imgs": "docs\\img", 6 | "project-name": "SimpleStudentCourseManagementSystem", 7 | "book-name": "Django 小实例S1 简易学生选课管理系统", 8 | "layer": "one-linked", 9 | "info": "docs\\info" 10 | 11 | }, 12 | "cnblogs":{ 13 | "url": "https://www.cnblogs.com", 14 | "api_url": "http://rpc.cnblogs.com/metaweblog/%s", 15 | "view_url": "%s/%s/p/%.html", 16 | "edit": "" 17 | } 18 | } -------------------------------------------------------------------------------- /docs/1.md: -------------------------------------------------------------------------------- 1 | ## 项目流程梳理与数据库设计 2 | 3 | ### 1 项目流程梳理 4 | 图示如下 5 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201111105628577.png) 6 | 7 | ### 2 课程状态 8 | 该项目的核心就是课程,这里梳理下课程的几个状态 9 | 1. 新建课程后 10 | 2. 开始选课后: 学生可自由选课撤课 11 | 3. 结束选课后: 不可选课不可撤课 12 | 4. 结课后: 老师先给学生打分,学生再给学生评价 13 | 5. 老师确认给分完毕 14 | 15 | ### 3 ER图 16 | > *E-R图也称实体-联系图(Entity Relationship Diagram)* 17 | 18 | er图示如下 19 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/2020111118073364.png) 20 | 由图可以看出 21 | 需要五个表: 22 | 23 | - 学生表 24 | - 教师表 25 | - 课程表 26 | - 学生课程表 27 | - 时刻表 28 | 29 | -------------------------------------------------------------------------------- /course/migrations/0004_auto_20201021_1720.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-10-21 09:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('course', '0003_auto_20200917_1644'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='schedule', 15 | name='remarks', 16 | field=models.CharField(max_length=100, null=True, verbose_name='备注'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /course/migrations/0005_auto_20201021_2109.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-10-21 13:09 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('course', '0004_auto_20201021_1720'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='schedule', 15 | name='remarks', 16 | field=models.CharField(blank=True, max_length=100, null=True, verbose_name='备注'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /course/migrations/0002_auto_20200813_1721.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-08-13 09:21 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('course', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='course', 15 | name='semester', 16 | field=models.CharField(choices=[('Autumn', '上'), ('Spring', '下')], max_length=20, verbose_name='学期'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /course/migrations/0006_auto_20201104_1036.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-11-04 02:36 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('course', '0005_auto_20201021_2109'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='studentcourse', 15 | name='rating', 16 | field=models.IntegerField(choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)], null=True, verbose_name='学生评分'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /static/css/form.css: -------------------------------------------------------------------------------- 1 | .create-update-from { 2 | margin: 10px; 3 | } 4 | 5 | .create-update-from p{ 6 | padding: 5px; 7 | } 8 | 9 | 10 | .create-update-from p:nth-child(even) { 11 | background-color: #dfdfdf; 12 | } 13 | 14 | .create-update-from p:nth-child(odd) { 15 | background-color: #c8c8d2; 16 | } 17 | 18 | .create-update-from p label{ 19 | display:inline-block; 20 | width: 200px; 21 | } 22 | 23 | .create-update-from .submit-button { 24 | margin-top: 20px; 25 | } 26 | 27 | .create-update-from .submit-button input { 28 | width: 80px; 29 | margin-right: 20px; 30 | } -------------------------------------------------------------------------------- /course/migrations/0007_auto_20201104_1040.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-11-04 02:40 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('course', '0006_auto_20201104_1036'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='studentcourse', 15 | name='rating', 16 | field=models.IntegerField(choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)], help_text='5分为最满意,最低分是1分', null=True, verbose_name='学生评分'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /templates/user/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% load static %} 4 | 5 | 6 | 7 | Register 8 | 9 | 10 | 11 | 12 | 13 |
14 |
注册
15 |
16 | {% csrf_token %} 17 | {{form.as_p}} 18 |

19 |
20 |
21 | -------------------------------------------------------------------------------- /static/css/details.css: -------------------------------------------------------------------------------- 1 | ul.course-details { 2 | margin: 20px; 3 | list-style: none; 4 | padding: 0 20px; 5 | } 6 | 7 | li.course-detail { 8 | min-height: 24px; 9 | padding: 2px; 10 | } 11 | 12 | li.course-detail .detail-name { 13 | display: inline-block; 14 | vertical-align: top; 15 | width: 150px; 16 | font-weight: bolder; 17 | } 18 | 19 | li.course-detail span.course-schedules { 20 | display: inline-block; 21 | } 22 | 23 | ul.course-details li:nth-child(odd) { 24 | background-color: #ccc; 25 | } 26 | 27 | ul.course-details li:nth-child(even) { 28 | background-color: #dfdfdf; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /templates/course/student/rating.html: -------------------------------------------------------------------------------- 1 | {% extends "course/nav.html" %} 2 | {% block title %}评教{% endblock %} 3 | {% block content %} 4 | {% load static %} 5 | 6 |

评教

7 |
8 |
9 | {% csrf_token %} 10 | {{form.as_p}} 11 |
12 | 13 | 14 |
15 |
16 |
17 | {% endblock %} -------------------------------------------------------------------------------- /templates/course/teacher/score.html: -------------------------------------------------------------------------------- 1 | {% extends "course/nav.html" %} 2 | {% block title %}{{ title }}{% endblock %} 3 | {% block content %} 4 | {% load static %} 5 | 6 |

{{ title }}

7 |
8 |
9 | {% csrf_token %} 10 | {{form.as_p}} 11 |
12 | 13 | 14 |
15 |
16 |
17 | {% endblock %} -------------------------------------------------------------------------------- /templates/course/teacher/create_course.html: -------------------------------------------------------------------------------- 1 | {% extends "course/nav.html" %} 2 | {% block title %}创建课程{% endblock %} 3 | {% block content %} 4 | {% load static %} 5 | 6 |

创建课程

7 |
8 |
9 | {% csrf_token %} 10 | {{form.as_p}} 11 |
12 | 13 | 14 |
15 |
16 |
17 | {% endblock %} -------------------------------------------------------------------------------- /templates/user/update.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% load static %} 4 | 5 | 6 | 7 | Register 8 | 9 | 10 | 11 | 12 | 13 |
14 |
修改个人信息
15 |
16 | {% csrf_token %} 17 | {{form.as_p}} 18 |

19 |
20 | 21 | 22 |
23 | -------------------------------------------------------------------------------- /templates/user/background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% load static %} 4 | 5 | 6 | 7 | 学生选课管理系统 8 | 9 | 10 | 11 | 12 |
13 |
14 |
学生选课管理系统
15 |
Student Course Management System
16 | {% block welcome_message %} 17 | {% endblock %} 18 |
19 | 23 |
24 | 25 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'scss.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /templates/course/teacher/create_schedule.html: -------------------------------------------------------------------------------- 1 | {% extends "course/nav.html" %} 2 | {% block title %}创建时刻表{% endblock %} 3 | {% block content %} 4 | {% load static %} 5 | 6 |

创建时刻表:    [{{ course.id }}] {{ course.name }}

7 |
8 |
9 | {% csrf_token %} 10 | {{form.as_p}} 11 |
12 | 13 | 14 |
15 |
16 |
17 | {% endblock %} -------------------------------------------------------------------------------- /course/migrations/0003_auto_20200917_1644.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-09-17 08:44 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('course', '0002_auto_20200813_1721'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='course', 15 | name='end_select', 16 | ), 17 | migrations.RemoveField( 18 | model_name='course', 19 | name='is_end', 20 | ), 21 | migrations.RemoveField( 22 | model_name='course', 23 | name='start_select', 24 | ), 25 | migrations.AddField( 26 | model_name='course', 27 | name='status', 28 | field=models.IntegerField(default=1, verbose_name='课程状态'), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## DOC catalog 2 | 3 | - 〇、初步介绍与演示 4 | - 一、项目流程梳理与数据库设计 5 | - 二、新建项目(project)并进行设置 6 | - 三、创建用户模型(model) 7 | - 四、实现登录页面 8 | - 五、实现注册功能 9 | - 六、实现登录逻辑 10 | - 七、修改个人信息 11 | - 八、css样式完善 12 | - 九、课程模块模型 13 | 14 | - 十、老师课程业务实现 15 | - 学生课程业务实现 16 | - css样式优化 17 | 18 | 19 | Django 采用了 MVT 的软件设计模式,即模型(Model),视图(View)和模板(Template) 20 | 21 | 主页的url 22 | 23 | 24 | ## 对学习笔记的更新 25 | - 第二章 完成 2-2 URL详细匹配规则 26 | - 插入新的第五章: 对模型对象进行增删查改 27 | 28 | --------------------- 29 | 30 | 31 | - 第四章model添加1:Model methods 32 | https://docs.djangoproject.com/en/2.2/topics/db/models/#meta-options 33 | 34 | https://docs.djangoproject.com/en/2.2/ref/models/options/#unique-together 35 | 36 | - 第四章model添加1*:Meta 元数据 37 | https://docs.djangoproject.com/en/2.2/topics/db/models/#meta-options 38 | 39 | 40 | 41 | 42 | 43 | #### 中间小思考 44 | - 脚本生成文本展示文件路径结构 45 | |--- 46 | 47 | 48 | 49 | 网站 50 | 51 | 像某个网站一样,查一下相关的案例 52 | 53 | OA 54 | 55 | 任务书,计划书 56 | 57 | 模块 58 | 用户模块 59 | - 前端 60 | - 后端 61 | - 数据库 62 | 课程模块 63 | -------------------------------------------------------------------------------- /templates/user/login_detail.html: -------------------------------------------------------------------------------- 1 | {% extends "user/background.html" %} 2 | {% block welcome_message %} 3 | {% if from_url == "register" %} 4 |
注册成功,你的{% if kind == "student" %}学号{% else %}账号{% endif %}是 {{ uid }}
5 | {% else %} 6 |
欢迎
7 | {% endif %} 8 | {% endblock %} 9 | {% block login_container %} 10 | {% if kind == "student" %} 11 | 12 | {% else %} 13 | 14 | {% endif %} 15 |
16 |
17 | {% csrf_token %} 18 | {{form.as_p}} 19 |
20 | 21 | 注册 22 |
23 |
24 | 25 |
26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /scss/urls.py: -------------------------------------------------------------------------------- 1 | # usr/bin/env python3 2 | # -*- coding:utf-8- -*- 3 | """scss URL Configuration 4 | 5 | The `urlpatterns` list routes URLs to views. For more information please see: 6 | https://docs.djangoproject.com/en/2.2/topics/http/urls/ 7 | Examples: 8 | Function views 9 | 1. Add an import: from my_app import views 10 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 11 | Class-based views 12 | 1. Add an import: from other_app.views import Home 13 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 14 | Including another URLconf 15 | 1. Import the include() function: from django.urls import include, path 16 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 17 | """ 18 | from django.contrib import admin 19 | from django.urls import path, include 20 | from user.views import login, home 21 | 22 | urlpatterns = [ 23 | path('admin/', admin.site.urls), 24 | path('user/', include("user.urls")), 25 | path('course/', include("course.urls")), 26 | 27 | path('', home) 28 | ] 29 | -------------------------------------------------------------------------------- /static/css/course_main.css: -------------------------------------------------------------------------------- 1 | .main-content { 2 | width: 900px; 3 | margin: 0 auto; 4 | background: #e6e6f0; 5 | height: 100%; 6 | position: fixed; 7 | left: 0; 8 | right: 0; 9 | padding: 60px 20px; 10 | top: 0; 11 | } 12 | 13 | .main-container { 14 | width: 850px; 15 | margin: 0 auto; 16 | } 17 | 18 | .main-content h3{ 19 | width: 850px; 20 | } 21 | 22 | .main-content .right-button{ 23 | float: right; 24 | margin: 0 5px; 25 | } 26 | 27 | .main-bar { 28 | width: 850px; 29 | height: 30px; 30 | } 31 | 32 | .main-bar .search-form { 33 | display: inline-block; 34 | } 35 | 36 | .main-bar .button { 37 | height: 30px; 38 | vertical-align:top; 39 | border:none; 40 | color:#eee; 41 | background:#4a2c98; 42 | font-size: 16px; 43 | border-radius: 5px; 44 | } 45 | 46 | .main-bar .input{ 47 | width: 150px; 48 | height: 24px; 49 | margin: auto 10px; 50 | vertical-align:top 51 | } 52 | 53 | .main-bar .right-button { 54 | float: right; 55 | margin: 0 5px; 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleStudentCourseManagementSystem 2 | Simple Student Course Management System, 简易学生选课管理系统 3 | 4 | During Production, 还在制作中 5 | 6 | ## Version 7 | #### Python version: 3.8.2 8 | #### Django version: 2.2.11 9 | install method: 10 | ```txt 11 | pip3 install Django==2.2.11 -i https://pypi.tuna.tsinghua.edu.cn/simple 12 | ``` 13 | 14 | ## TODO 15 | - student opeartion 16 | - search course 17 | - form css 18 | 19 | 20 | 21 | python manage.py makemigrations 22 | python manage.py migrate 23 | 24 | ## TIPS 25 | #### account 26 | Teahcer: 27 | 任猎城 28 | u: 1280000001 29 | p: 12345678 30 | 镇天稽 31 | u: 1110000001 32 | p: 22334455 33 | 爱嘤诗贝伦 34 | u: 1160000001 35 | p: 12341234 36 | 牛有力 37 | u: 2660000001 38 | p: password 39 | 40 | 41 | 42 | Student: 43 | 李大爽 44 | u: 2020000001 45 | p: libigshuang 46 | 47 | 张三 48 | u: 2018000001 49 | p: zhang333 50 | 51 | 52 | 2044000001 53 | three12345 54 | 55 | 2020000002 56 | xiaored 57 | 58 | 59 | 60 | ## Problems 61 | #### 1 如何给Class-Based Views 的as_view()生成的view方法里面传参。 62 | 比如UpdateTeacherView和UpdateStudentView里面获取不到view方法传入的其他参数 63 | 解决方法: 重写get_context_data(), 在里面先写入固定的参数 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 BigShuang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /templates/course/nav.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% load static %} 4 | 5 | 6 | 7 | {% block title %}{% endblock %} 8 | 9 | 10 | 11 | 12 | 13 | 37 | 38 |
39 | {% block content %}{% endblock %} 40 |
41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/0.md: -------------------------------------------------------------------------------- 1 | ## 初步介绍与演示 2 | ### 1 初步介绍 3 | 先介绍下这个项目的几个特点 4 | 5 | 1. 无图片文件,背景样式都是用css完成,且都是些比较简单的css。 6 | 2. 专注于后端,无需额外安装前端框架,且无JS文件, 只用了最简单的两行JS代码作为onlick调用的事件 7 | ```javascript 8 | location.href=url // 跳转到url 9 | window.open(url) // 在新标签页中打开url 10 | ``` 11 | 12 | 3. 这是本人第一个做的Django小项目, 13 | - 缺点: 很多地方不是很成熟 14 | - 优点: 有各种有意思的尝试 15 | 16 | 由于1、2的缘故,整体网页样式上比较简陋,但是避免了在前端上过多的花费心思,能集中心思于理解后端。 17 | 18 | **技术栈上** 19 | 前端:html/css 20 | 后端: python Django 21 | 数据库:sqlite3, 会直接在项目文件夹内生成db文件 22 | 23 | ### 2 教程梳理 24 | 在这篇文章所在的专栏里,我会将本项目的过程与代码,按照一个比较好理解的步骤梳理展示出来。 25 | 每一个相对独立的大步骤会成为一篇文章。 26 | 27 | 不过由于篇幅的缘故,关于代码部分,我只讲解业务逻辑部分。 28 | 29 | 代码本身所涉及的相关基础知识,需要读者自己去学习。 30 | 31 | 当然我会尽量在每一篇文章中,概括下将用到的python的、Django的知识点,方便读者去搜索学习。 32 | 33 | 同时对于这些知识点,也会推荐一些教程(一般为官方文档或者我的[Django自学笔记](https://blog.csdn.net/python1639er/category_9826344.html)) 34 | 35 | 36 | ### 3 展示 37 | 视频展示见本人b站投稿:[https://www.bilibili.com/video/BV1er4y1w7ty?p=2](https://www.bilibili.com/video/BV1er4y1w7ty?p=2) 38 | 39 | 这里仅展示该项目几个有代表性的截图页面 40 | 41 | - 主页——选择身份登录 42 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201111103007452.png) 43 | - 注册页面 44 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201111103046125.png) 45 | - 教师主页——课程列表页 46 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201111103259953.png) 47 | - 教师课程详情页 48 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201111103333292.png) 49 | -------------------------------------------------------------------------------- /static/css/refer.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | width: 100%; 4 | height:100%; 5 | font-family: 'Open Sans', sans-serif; 6 | background: #092756; 7 | background: -moz-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%),-moz-linear-gradient(top, rgba(57,173,219,.25) 0%, rgba(42,60,87,.4) 100%), -moz-linear-gradient(-45deg, #670d10 0%, #092756 100%); 8 | background: -webkit-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -webkit-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -webkit-linear-gradient(-45deg, #670d10 0%,#092756 100%); 9 | background: -o-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -o-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -o-linear-gradient(-45deg, #670d10 0%,#092756 100%); 10 | background: -ms-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), -ms-linear-gradient(top, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), -ms-linear-gradient(-45deg, #670d10 0%,#092756 100%); 11 | background: -webkit-radial-gradient(0% 100%, ellipse cover, rgba(104,128,138,.4) 10%,rgba(138,114,76,0) 40%), linear-gradient(to bottom, rgba(57,173,219,.25) 0%,rgba(42,60,87,.4) 100%), linear-gradient(135deg, #670d10 0%,#092756 100%); 12 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3E1D6D', endColorstr='#092756',GradientType=1 ); 13 | } 14 | -------------------------------------------------------------------------------- /static/css/nav.css: -------------------------------------------------------------------------------- 1 | body, 2 | p { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | html,body { 8 | height: 100%; 9 | width: 100%; 10 | } 11 | 12 | body { 13 | /*background: #e6e6f0;*/ 14 | background: #ccc; 15 | } 16 | 17 | 18 | .nav { 19 | background: #4a2c98; 20 | position: fixed; 21 | width: 100%; 22 | color: #ccc; 23 | z-index: 1; 24 | } 25 | 26 | 27 | .nav .nav-title, 28 | .nav .name-logo { 29 | display: inline-block; 30 | margin: 5px; 31 | } 32 | 33 | .nav p { 34 | display: inline-block; 35 | float: left; 36 | padding-left: 10px; 37 | } 38 | 39 | 40 | .nav .nav-title{ 41 | font-size: 24px; 42 | line-height: 26px; 43 | height: 26px; 44 | vertical-align: top; 45 | 46 | } 47 | 48 | .nav .nav-title a { 49 | color: unset; 50 | text-decoration: unset; 51 | } 52 | 53 | .nav p.main-title{ 54 | margin-right: 10px; 55 | } 56 | 57 | .nav p.sub-title { 58 | border-left: 3px solid #cccccc; 59 | } 60 | 61 | .nav .name-logo { 62 | float: right; 63 | margin-top: 8px; 64 | } 65 | 66 | .nav .name-logo .user-name { 67 | background: #ccc; 68 | border-radius: 50%; 69 | width: 24px; 70 | height: 24px; 71 | color: #4a2c98; 72 | text-align: center; 73 | line-height: 24px; 74 | font-size: 16px; 75 | font-weight: bold; 76 | } 77 | 78 | .nav .name-logo .user-name a { 79 | text-decoration: none; 80 | } 81 | 82 | .nav .log-out { 83 | display: inline-block; 84 | float: right; 85 | margin: 9px 5px 0; 86 | } 87 | 88 | .nav .log-out a { 89 | margin: 5px; 90 | background: #ccc; 91 | border-radius: 5px; 92 | text-decoration: none; 93 | padding: 0 5px; 94 | } 95 | 96 | -------------------------------------------------------------------------------- /static/css/list.css: -------------------------------------------------------------------------------- 1 | table.item-list { 2 | margin: 10px 0; 3 | width: 850px; 4 | } 5 | 6 | .item-list th, 7 | .item-list td { 8 | box-sizing: content-box; 9 | width: fit-content; 10 | padding: 3px; 11 | text-align: left; 12 | border-bottom: 1px solid #C0C0C0; 13 | } 14 | 15 | 16 | .item-list tr:nth-child(even) { 17 | background-color: #dfdfdf; 18 | } 19 | 20 | .item-list th { 21 | background-color: #9481c5; 22 | } 23 | 24 | 25 | 26 | /* for course table col width*/ 27 | .item-list th.course-no, 28 | .item-list td.course-no { 29 | width: 70px; 30 | } 31 | 32 | .item-list th.course-name, 33 | .item-list td.course-name { 34 | width: 150px; 35 | } 36 | 37 | .item-list th.course-credit, 38 | .item-list td.course-credit { 39 | width: 40px; 40 | } 41 | 42 | .item-list th.course-number, 43 | .item-list td.course-number { 44 | width: 70px; 45 | } 46 | 47 | .item-list th.course-year, 48 | .item-list td.course-year { 49 | width: 50px; 50 | } 51 | 52 | .item-list th.course-semester, 53 | .item-list td.course-semester { 54 | width: 30px; 55 | } 56 | 57 | .item-list th.course-status, 58 | .item-list td.course-status { 59 | width: 100px; 60 | } 61 | 62 | .item-list th.course-teacher, 63 | .item-list td.course-teacher { 64 | width: 70px; 65 | } 66 | 67 | .item-list th.course-operation, 68 | .item-list td.course-operation { 69 | width: 150px; 70 | } 71 | 72 | .item-list th.course-schedule, 73 | .item-list td.course-schedule { 74 | width: 200px 75 | } 76 | 77 | .item-list td.course-schedule { 78 | font-size: 10px; 79 | } 80 | 81 | .item-list th.course-operation.student-course, 82 | .item-list td.course-operation.student-course { 83 | width: 80px; 84 | } 85 | 86 | .item-list th.course-year-semester, 87 | .item-list td.course-year-semester { 88 | width: 70px; 89 | } -------------------------------------------------------------------------------- /course/urls.py: -------------------------------------------------------------------------------- 1 | """scss URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include 18 | from course.views import * 19 | from course.cbvs import ScheduleDeleteView, ScoreUpdateView, RateUpdateView, StudentCourseDetailView 20 | 21 | urlpatterns = [ 22 | path('', to_home, name="course"), 23 | path('/', home, name="course"), 24 | path('teacher/create_course', create_course, name="create_course"), 25 | path('teacher/view_detail/', view_detail, name="view_detail"), 26 | path('teacher/create_schedule/', create_schedule, name="create_schedule"), 27 | path('teacher/delete_schedule/', delete_schedule, name="delete_schedule"), 28 | path('teacher/score/', ScoreUpdateView.as_view(), name="score"), 29 | path('teacher/handle_course//', handle_course, name="handle_course"), 30 | 31 | path('student/view/', view_course, name="view_course"), 32 | path('student/operate//', operate_course, name="operate_course"), 33 | 34 | path('student/evaluate/', RateUpdateView.as_view(), name="evaluate"), 35 | path('student/view_detail/', StudentCourseDetailView.as_view(), name="sview_detail"), 36 | ] 37 | -------------------------------------------------------------------------------- /course/forms.py: -------------------------------------------------------------------------------- 1 | # usr/bin/env python 2 | # -*- coding:utf-8- -*- 3 | from django import forms 4 | from .models import Course, Schedule, StudentCourse 5 | 6 | 7 | class CourseForm(forms.ModelForm): 8 | 9 | class Meta: 10 | model = Course 11 | exclude = ['status', 'teacher'] 12 | 13 | 14 | class ScheduleForm(forms.ModelForm): 15 | class Meta: 16 | model = Schedule 17 | exclude = ["course"] 18 | 19 | 20 | class ScoreForm(forms.ModelForm): 21 | class Meta: 22 | model = StudentCourse 23 | fields = ["student", "course", "scores", "comments"] 24 | 25 | student = forms.CharField(label="学生", disabled=True) 26 | # course = forms.CharField(widget=forms.TextInput(attrs={'readonly': 'readonly'})) 27 | course = forms.CharField(label="课程", disabled=True) 28 | 29 | def __init__(self, *args, **kwargs): 30 | super().__init__(*args, **kwargs) 31 | self.initial['student'] = self.instance.student 32 | self.initial['course'] = self.instance.course 33 | 34 | def clean_student(self): 35 | return self.initial['student'] 36 | 37 | def clean_course(self): 38 | return self.initial['course'] 39 | 40 | 41 | class RateForm(forms.ModelForm): 42 | class Meta: 43 | model = StudentCourse 44 | fields = ["course", "scores", "comments", "rating", "assessment"] 45 | 46 | course = forms.CharField(label="课程", disabled=True) 47 | scores = forms.IntegerField(label="成绩", disabled=True) 48 | comments = forms.CharField(label="老师评价", disabled=True) 49 | 50 | def __init__(self, *args, **kwargs): 51 | super().__init__(*args, **kwargs) 52 | self.initial['course'] = self.instance.course 53 | self.initial['scores'] = self.instance.scores 54 | self.initial['comments'] = self.instance.comments 55 | 56 | def clean_course(self): 57 | return self.initial['course'] 58 | 59 | def clean_scores(self): 60 | return self.initial['scores'] 61 | 62 | def clean_comments(self): 63 | return self.initial['comments'] 64 | -------------------------------------------------------------------------------- /user/util.py: -------------------------------------------------------------------------------- 1 | # usr/bin/env python 2 | # -*- coding:utf-8- -*- 3 | from django.http.response import HttpResponse 4 | from django.shortcuts import reverse, redirect 5 | 6 | from constants import * 7 | from user.models import Student, Teacher 8 | 9 | 10 | def check_login(func): 11 | # the func method must have the second parameter kind. 12 | def _check(*args, **kwargs): 13 | request = args[1] 14 | cookie_kind = request.session.get('kind', '') 15 | if cookie_kind not in ["student", "teacher"]: 16 | # Not logged in 17 | to_url = reverse("login") 18 | return redirect(to_url) 19 | elif len(args) >= 2: 20 | # the second parameter must be kind 21 | kind = args[1] 22 | if kind == cookie_kind: 23 | return func(*args, **kwargs) 24 | else: 25 | return HttpResponse(ILLEGAL_KIND) 26 | return HttpResponse(INVALID_URL) 27 | 28 | return _check 29 | 30 | 31 | def get_user(request, kind): 32 | """ 33 | 34 | :param request: 35 | :param kind: teacher or student 36 | :return: return Teacher instance or Student instance 37 | """ 38 | if request.session.get('kind', '') != kind or kind not in ["student", "teacher"]: 39 | return None 40 | 41 | if len(request.session.get('user', '')) != 10: 42 | return None 43 | 44 | uid = request.session.get('user') 45 | if kind == "student": 46 | # 找到对应学生 47 | grade = uid[:4] 48 | number = uid[4:] 49 | student_set = Student.objects.filter(grade=grade, number=number) 50 | if student_set.count() == 0: 51 | return None 52 | return student_set[0] 53 | else: 54 | # 找到对应老师 55 | department_no = uid[:3] 56 | number = uid[3:] 57 | teacher_set = Teacher.objects.filter(department_no=department_no, number=number) 58 | if teacher_set.count() == 0: 59 | return None 60 | return teacher_set[0] 61 | 62 | 63 | -------------------------------------------------------------------------------- /user/models.py: -------------------------------------------------------------------------------- 1 | # usr/bin/env python3 2 | # -*- coding:utf-8- -*- 3 | from django.db import models 4 | 5 | 6 | class Student(models.Model): 7 | gender = [ 8 | ("m", "男"), 9 | ("f", "女") 10 | ] 11 | 12 | name = models.CharField(max_length=50, verbose_name="姓名") 13 | gender = models.CharField(max_length=10, choices=gender, default='m', verbose_name="性别") 14 | birthday = models.DateField(verbose_name="生日") 15 | email = models.EmailField(verbose_name="邮箱") 16 | info = models.CharField(max_length=255, verbose_name="个人简介", help_text="一句话介绍自己,不要超过250字") 17 | 18 | grade = models.CharField(max_length=4, verbose_name="年级") 19 | number = models.CharField(max_length=6, verbose_name="年级子学号") 20 | password = models.CharField(max_length=30, verbose_name="密码") 21 | 22 | class Meta: 23 | constraints = [ 24 | # 复合主键:保证 grade和number组合的student_id唯一 25 | models.UniqueConstraint(fields=['grade', 'number'], name='student_id'), 26 | ] 27 | 28 | def get_id(self): 29 | return self.grade + self.number 30 | 31 | def __str__(self): 32 | return "%s (%s)" % (self.grade + self.number, self.name) 33 | 34 | 35 | class Teacher(models.Model): 36 | genders = [ 37 | ("m", "男"), 38 | ("f", "女") 39 | ] 40 | 41 | name = models.CharField(max_length=50, verbose_name="姓名") 42 | gender = models.CharField(max_length=10, choices=genders, default='m', verbose_name="性别") 43 | birthday = models.DateField(verbose_name="生日") 44 | email = models.EmailField(verbose_name="邮箱") 45 | info = models.CharField(max_length=255, verbose_name="教师简介", help_text="不要超过250字") 46 | 47 | department_no = models.CharField(max_length=3, verbose_name="院系号") 48 | number = models.CharField(max_length=7, verbose_name="院内编号") 49 | password = models.CharField(max_length=30, verbose_name="密码") 50 | 51 | class Meta: 52 | constraints = [ 53 | # 复合主键:保证 department_no 和number组合的 teacher_id 唯一 54 | models.UniqueConstraint(fields=['department_no', 'number'], name='teacher_id'), 55 | ] 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /static/css/register.css: -------------------------------------------------------------------------------- 1 | .register-container { 2 | height: 40%; 3 | width: 500px; 4 | margin: 100px auto; 5 | background: #eee; 6 | border-radius: 10px; 7 | box-shadow: 0 0 15px 2px rgba(0,0,0,.33); 8 | overflow: hidden; 9 | } 10 | 11 | 12 | .register-container .register-title { 13 | height: auto; 14 | padding: 2%; 15 | justify-content: center; 16 | text-align: center; 17 | color: #ccc; 18 | width: 96%; 19 | font-size: 22px; 20 | display: block; 21 | background: #4d2f99; 22 | overflow: hidden; 23 | } 24 | 25 | 26 | .register-container .form p { 27 | width: 90%; 28 | padding-top: 15px; 29 | margin: 0 auto; 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | font-family: "Roboto","Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif; 34 | word-break: break-all; 35 | flex-flow: wrap; 36 | } 37 | 38 | .register-container .form p label { 39 | padding-right: 10px; 40 | width: 80px; 41 | } 42 | 43 | .register-container .form p input, 44 | .register-container .form p select { 45 | clear: both; 46 | padding: 8px; 47 | width: 60%; 48 | -webkit-box-sizing: border-box; 49 | -moz-box-sizing: border-box; 50 | box-sizing: border-box; 51 | border: 1px solid #ccc; 52 | border-radius: 4px; 53 | } 54 | 55 | .register-container .form p span.helptext { 56 | color: slategrey; 57 | } 58 | 59 | .register-container .form p .submit-button { 60 | border: none; 61 | text-decoration: none; 62 | font-size: 18px; 63 | background: #4a2c98; 64 | color: #eeeeee; 65 | padding: 5px 0; 66 | text-align: center; 67 | display: block; 68 | width: 30%; 69 | margin: 0 10px 20px; 70 | border-radius: 10px; 71 | } 72 | 73 | 74 | .update-container { 75 | background: none; 76 | border-radius: 0; 77 | box-shadow: none; 78 | margin: 10px auto; 79 | } 80 | 81 | 82 | .register-container .return-button { 83 | padding-left: 20px; 84 | padding-bottom: 10px; 85 | } 86 | -------------------------------------------------------------------------------- /templates/course/student/course.html: -------------------------------------------------------------------------------- 1 | {% extends "course/nav.html" %} 2 | {% block title %}课程详情{% endblock %} 3 | {% block content %} 4 | {% load static %} 5 | 6 |

课程详情

7 |
    8 |
  • 课程编号 {{ object.course.id }}
  • 9 |
  • 课程名 {{ object.course.name }}
  • 10 |
  • 学分 {{ object.course.credit }}
  • 11 |
  • 课程人数/最大人数 {{ object.course.get_current_count }}/{{ object.course.max_number }}
  • 12 |
  • 年份 {{ object.course.year }}
  • 13 |
  • 学期 {{ object.course.get_semester_display }}
  • 14 | 15 |
  • 教师 {{ object.course.teacher.name }}
  • 16 |
  • 上课时间 17 | 18 | {% for schedule in object.course.get_schedules %} 19 |
    {{ schedule }}
    20 | {% endfor %} 21 |
    22 |
  • 23 |
  • 得分 24 | {% if object.scores != None %}{{ object.scores }}{% else %} - {% endif %} 25 |
  • 26 |
  • 评语 27 | {% if object.comments != None %}{{ object.comments }}{% else %} - {% endif %} 28 |
  • 29 |
  • 学生评分 30 | {% if object.rating != None %}{{ object.rating }}{% else %} - {% endif %} 31 |
  • 32 |
  • 学生评价 33 | {% if object.assessment != None %}{{ object.assessment }}{% else %} - {% endif %} 34 |
  • 35 | 36 |
37 | 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /user/forms.py: -------------------------------------------------------------------------------- 1 | # usr/bin/env python 2 | # -*- coding:utf-8- -*- 3 | from django import forms 4 | from .models import Student, Teacher 5 | 6 | 7 | class StuLoginForm(forms.Form): 8 | uid = forms.CharField(label='学号', max_length=10) 9 | password = forms.CharField(label='密码', max_length=30, widget=forms.PasswordInput) 10 | 11 | 12 | class TeaLoginForm(forms.Form): 13 | uid = forms.CharField(label='教职工号', max_length=10) 14 | password = forms.CharField(label='密码', max_length=30, widget=forms.PasswordInput) 15 | 16 | 17 | class StuRegisterForm(forms.ModelForm): 18 | confirm_password = forms.CharField(widget=forms.PasswordInput(), label="确认密码") 19 | 20 | class Meta: 21 | model = Student 22 | fields = ('grade', 23 | 'name', 24 | 'password', 25 | 'confirm_password', 26 | 'gender', 27 | 'birthday', 28 | 'email', 29 | 'info') 30 | 31 | def clean(self): 32 | cleaned_data = super(StuRegisterForm, self).clean() 33 | password = cleaned_data.get('password') 34 | confirm_password = cleaned_data.get('confirm_password') 35 | if confirm_password != password: 36 | self.add_error('confirm_password', 'Password does not match.') 37 | 38 | return cleaned_data 39 | 40 | 41 | class StuUpdateForm(StuRegisterForm): 42 | class Meta: 43 | model = Student 44 | fields = ('name', 45 | 'password', 46 | 'confirm_password', 47 | 'gender', 48 | 'birthday', 49 | 'email', 50 | 'info') 51 | 52 | 53 | class TeaRegisterForm(forms.ModelForm): 54 | confirm_password = forms.CharField(widget=forms.PasswordInput(), label="确认密码") 55 | 56 | class Meta: 57 | model = Teacher 58 | fields = ('name', 59 | 'password', 60 | 'confirm_password', 61 | 'gender', 62 | 'birthday', 63 | 'email', 64 | 'info') 65 | 66 | def clean(self): 67 | cleaned_data = super(TeaRegisterForm, self).clean() 68 | password = cleaned_data.get('password') 69 | confirm_password = cleaned_data.get('confirm_password') 70 | if confirm_password != password: 71 | self.add_error('confirm_password', 'Password does not match.') -------------------------------------------------------------------------------- /user/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-08-11 13:05 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Student', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=50, verbose_name='姓名')), 19 | ('gender', models.CharField(choices=[('m', '男'), ('f', '女')], default='m', max_length=10, verbose_name='性别')), 20 | ('birthday', models.DateField(verbose_name='生日')), 21 | ('email', models.EmailField(max_length=254, verbose_name='邮箱')), 22 | ('info', models.CharField(help_text='一句话介绍自己,不要超过250字', max_length=255, verbose_name='个人简介')), 23 | ('grade', models.CharField(max_length=4, verbose_name='年级')), 24 | ('number', models.CharField(max_length=6, verbose_name='年级子学号')), 25 | ('password', models.CharField(max_length=30, verbose_name='密码')), 26 | ], 27 | ), 28 | migrations.CreateModel( 29 | name='Teacher', 30 | fields=[ 31 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 32 | ('name', models.CharField(max_length=50, verbose_name='姓名')), 33 | ('gender', models.CharField(choices=[('m', '男'), ('f', '女')], default='m', max_length=10, verbose_name='性别')), 34 | ('birthday', models.DateField(verbose_name='生日')), 35 | ('email', models.EmailField(max_length=254, verbose_name='邮箱')), 36 | ('info', models.CharField(help_text='不要超过250字', max_length=255, verbose_name='教师简介')), 37 | ('department_no', models.CharField(max_length=3, verbose_name='院系号')), 38 | ('number', models.CharField(max_length=7, verbose_name='院内编号')), 39 | ('password', models.CharField(max_length=30, verbose_name='密码')), 40 | ], 41 | ), 42 | migrations.AddConstraint( 43 | model_name='teacher', 44 | constraint=models.UniqueConstraint(fields=('department_no', 'number'), name='teacher_id'), 45 | ), 46 | migrations.AddConstraint( 47 | model_name='student', 48 | constraint=models.UniqueConstraint(fields=('grade', 'number'), name='student_id'), 49 | ), 50 | ] 51 | -------------------------------------------------------------------------------- /course/cbvs.py: -------------------------------------------------------------------------------- 1 | from django.views.generic.edit import DeleteView, CreateView, UpdateView 2 | from django.views.generic.detail import DetailView 3 | from django.shortcuts import render, reverse, redirect 4 | 5 | # Relative import of GeeksModel 6 | from .models import Schedule, StudentCourse 7 | from .forms import ScoreForm, RateForm 8 | 9 | 10 | class ScheduleDeleteView(DeleteView): 11 | model = Schedule 12 | 13 | 14 | class ScoreUpdateView(UpdateView): 15 | model = StudentCourse 16 | form_class = ScoreForm 17 | template_name = 'course/teacher/score.html' 18 | 19 | def get(self, request, *args, **kwargs): 20 | self.object = self.get_object() 21 | 22 | title = "给分" 23 | if request.GET.get("update"): 24 | title = "修改成绩" 25 | 26 | info = {} 27 | return_url = reverse("course", kwargs={"kind": "teacher"}) 28 | if self.object: 29 | teacher = self.object.course.teacher 30 | info = { 31 | "name": teacher.name, 32 | "kind": "teacher", 33 | } 34 | return_url = reverse("view_detail", kwargs={"course_id": self.object.course.id}) 35 | 36 | return self.render_to_response(self.get_context_data(info=info, title=title, return_url=return_url)) 37 | 38 | def get_success_url(self): 39 | if self.object: 40 | return reverse("view_detail", kwargs={"course_id": self.object.course.id}) 41 | else: 42 | return reverse("course", kwargs={"kind": "teacher"}) 43 | 44 | 45 | class RateUpdateView(UpdateView): 46 | model = StudentCourse 47 | form_class = RateForm 48 | template_name = 'course/student/rating.html' 49 | 50 | def get(self, request, *args, **kwargs): 51 | self.object = self.get_object() 52 | 53 | info = {} 54 | return_url = reverse("view_course", kwargs={"view_kind": "is_end"}) 55 | if self.object: 56 | student = self.object.student 57 | info = { 58 | "name": student.name, 59 | "kind": "student", 60 | } 61 | 62 | return self.render_to_response(self.get_context_data(info=info, return_url=return_url)) 63 | 64 | def get_success_url(self): 65 | return reverse("view_course", kwargs={"view_kind": "is_end"}) 66 | 67 | 68 | class StudentCourseDetailView(DetailView): 69 | model = StudentCourse 70 | template_name = 'course/student/course.html' 71 | 72 | def get(self, request, *args, **kwargs): 73 | self.object = self.get_object() 74 | context = self.get_context_data(object=self.object) 75 | if self.object: 76 | context["info"] = { 77 | "name": self.object.student.name, 78 | "kind": "student", 79 | } 80 | return self.render_to_response(context) 81 | -------------------------------------------------------------------------------- /templates/course/teacher/home.html: -------------------------------------------------------------------------------- 1 | {% extends "course/nav.html" %} 2 | {% block title %}HomePage{% endblock %} 3 | {% block content %} 4 | {% load static %} 5 | 6 |
7 |
8 |
9 | {% csrf_token %} 10 | 11 | 12 |
13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {% for course in course_list %} 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 53 | 54 | {% endfor %} 55 | 56 | 57 |
课程编号名称学分当前人数
/总人数
年份学期状态操作
{{ course.id }}{{ course.name }}{{ course.credit }}{{ course.get_current_count }}/{{ course.max_number }}{{ course.year }}{{ course.get_semester_display }}{{ course.get_status_text }} 40 | {% if course.status < 4 %} 41 | 43 | {% endif %} 44 | {% if course.status == 4 %} 45 | {# 结课后给分 #} 46 | 48 | {% else %} 49 | 51 | {% endif %} 52 |
58 |
59 | {% endblock %} -------------------------------------------------------------------------------- /static/css/login.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | .main-container { 6 | position: absolute; 7 | width: 100%; 8 | height: 100%; 9 | background: #4a2c964d; 10 | background: linear-gradient(rgba(230, 100, 101, 0.2), rgba(145, 152, 229, 0.3)), linear-gradient(#9198e560, #4a2c9880); 11 | } 12 | 13 | .main-header { 14 | height: 40%; 15 | text-align: center; 16 | font-size: 40px; 17 | color: #4a2c98;; 18 | } 19 | 20 | .main-header .main-title { 21 | font-size: 50px; 22 | padding-top: 5%; 23 | } 24 | 25 | .main-header .sub-tile { 26 | padding: 1%; 27 | color: #25c5ff 28 | } 29 | 30 | .main-header .welcome-message { 31 | font-size: 26px; 32 | margin-top: 60px; 33 | color: #ff5722; 34 | } 35 | 36 | .login-container { 37 | height: 40%; 38 | width: 400px; 39 | margin: 0 auto; 40 | background: #eee; 41 | border-radius: 10px; 42 | box-shadow: 0 0 15px 2px rgba(0,0,0,.33); 43 | overflow: hidden; 44 | } 45 | 46 | 47 | .login-container .login-kind{ 48 | padding-top: 10%; 49 | font-size: 30px; 50 | } 51 | 52 | 53 | .login-container .login-kind a { 54 | text-decoration: none; 55 | background: #4a2c98; 56 | color: #eeeeee; 57 | padding: 10px; 58 | text-align: center; 59 | display: block; 60 | width: 50%; 61 | margin: 0 auto; 62 | border-radius: 10px; 63 | } 64 | 65 | .login-container .student-login-button{ 66 | } 67 | 68 | .login-container .teacher-login-button{ 69 | } 70 | 71 | .hidden-kind { 72 | display: none; 73 | } 74 | 75 | 76 | .teacher-form { 77 | display: none; 78 | } 79 | 80 | 81 | .login-kind-title { 82 | height: auto; 83 | padding: 2%; 84 | justify-content: center; 85 | text-align: center; 86 | color: #4d2f99; 87 | width: 96%; 88 | font-size: 22px; 89 | display: block; 90 | background: #ccc; 91 | overflow: hidden; 92 | } 93 | 94 | 95 | .login-container .form p, 96 | .login-container .form .submit-button{ 97 | width: 90%; 98 | padding-top: 4%; 99 | margin: 0 auto; 100 | display: flex; 101 | align-items: center; 102 | justify-content: center; 103 | font-family: "Roboto","Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif; 104 | } 105 | 106 | .login-container .form p label { 107 | padding-right: 10px; 108 | width: 80px; 109 | } 110 | 111 | .login-container .form p input { 112 | clear: both; 113 | padding: 8px; 114 | width: 60%; 115 | -webkit-box-sizing: border-box; 116 | -moz-box-sizing: border-box; 117 | box-sizing: border-box; 118 | border: 1px solid #ccc; 119 | border-radius: 4px; 120 | } 121 | 122 | .login-container .form .submit-button , 123 | .login-container .return-button{ 124 | margin: 5px auto 0; 125 | padding-top: 20px; 126 | } 127 | 128 | .submit-button input, 129 | .submit-button a { 130 | border: none; 131 | text-decoration: none; 132 | font-size: 18px; 133 | background: #4a2c98; 134 | color: #eeeeee; 135 | padding: 5px 0; 136 | text-align: center; 137 | display: block; 138 | width: 30%; 139 | margin: 5px 10px; 140 | border-radius: 10px; 141 | } 142 | 143 | .return-button a { 144 | border: none; 145 | text-decoration: none; 146 | font-size: 18px; 147 | background: #cccccc; 148 | color: #4a2c98; 149 | padding: 5px 0; 150 | text-align: center; 151 | display: block; 152 | width: 30%; 153 | margin: 0 auto; 154 | border-radius: 10px; 155 | } 156 | 157 | 158 | -------------------------------------------------------------------------------- /course/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.11 on 2020-08-11 13:05 2 | 3 | import course.models 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ('user', '0001_initial'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Course', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('name', models.CharField(max_length=50, verbose_name='课程名')), 22 | ('introduction', models.CharField(max_length=250, verbose_name='介绍')), 23 | ('credit', models.IntegerField(verbose_name='学分')), 24 | ('max_number', models.IntegerField(verbose_name='课程最大人数')), 25 | ('year', models.IntegerField(default=course.models.current_year, verbose_name='年份')), 26 | ('semester', models.CharField(choices=[('Autumn', '上'), ('Spring', '下')], max_length=5, verbose_name='学期')), 27 | ('start_select', models.BooleanField(default=False, verbose_name='开始选课')), 28 | ('end_select', models.BooleanField(default=False, verbose_name='结束选课')), 29 | ('is_end', models.BooleanField(default=False, verbose_name='是否结课')), 30 | ('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='user.Teacher', verbose_name='课程教师')), 31 | ], 32 | ), 33 | migrations.CreateModel( 34 | name='StudentCourse', 35 | fields=[ 36 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 37 | ('create_time', models.DateTimeField(auto_now=True)), 38 | ('with_draw', models.BooleanField(default=False)), 39 | ('with_draw_time', models.DateTimeField(default=None, null=True)), 40 | ('scores', models.IntegerField(null=True, verbose_name='成绩')), 41 | ('comments', models.CharField(max_length=250, null=True, verbose_name='老师评价')), 42 | ('rating', models.IntegerField(null=True, verbose_name='学生评分')), 43 | ('assessment', models.CharField(max_length=250, null=True, verbose_name='学生评价')), 44 | ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course.Course')), 45 | ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='user.Student')), 46 | ], 47 | ), 48 | migrations.CreateModel( 49 | name='Schedule', 50 | fields=[ 51 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 52 | ('weekday', models.IntegerField(choices=[(1, '周一'), (2, '周二'), (3, '周三'), (4, '周四'), (5, '周五'), (6, '周六'), (7, '周日')], verbose_name='日期')), 53 | ('start_time', models.TimeField(verbose_name='上课时间')), 54 | ('end_time', models.TimeField(verbose_name='下课时间')), 55 | ('location', models.CharField(max_length=100, verbose_name='上课地点')), 56 | ('remarks', models.CharField(max_length=100, verbose_name='备注')), 57 | ('start_week', models.IntegerField(verbose_name='第几周开始')), 58 | ('end_week', models.IntegerField(verbose_name='第几周结束')), 59 | ('week_interval', models.IntegerField(choices=[(1, '无间隔'), (2, '每隔一周上一次')], default=1, verbose_name='周间隔')), 60 | ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course.Course', verbose_name='课程名')), 61 | ], 62 | ), 63 | ] 64 | -------------------------------------------------------------------------------- /scss/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for scss project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.2.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.2/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'bx(&2k2&0dz9g+@#58wovl-19a^kl*xo)5tiem&vmwfxb(qr&%' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'user', 41 | 'course' 42 | ] 43 | 44 | MIDDLEWARE = [ 45 | 'django.middleware.security.SecurityMiddleware', 46 | 'django.contrib.sessions.middleware.SessionMiddleware', 47 | 'django.middleware.common.CommonMiddleware', 48 | 'django.middleware.csrf.CsrfViewMiddleware', 49 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 50 | 'django.contrib.messages.middleware.MessageMiddleware', 51 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 52 | ] 53 | 54 | ROOT_URLCONF = 'scss.urls' 55 | 56 | TEMPLATES = [ 57 | { 58 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 59 | 'DIRS': [os.path.join(BASE_DIR, 'templates')] 60 | , 61 | 'APP_DIRS': True, 62 | 'OPTIONS': { 63 | 'context_processors': [ 64 | 'django.template.context_processors.debug', 65 | 'django.template.context_processors.request', 66 | 'django.contrib.auth.context_processors.auth', 67 | 'django.contrib.messages.context_processors.messages', 68 | ], 69 | }, 70 | }, 71 | ] 72 | 73 | WSGI_APPLICATION = 'scss.wsgi.application' 74 | 75 | 76 | # Database 77 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases 78 | 79 | DATABASES = { 80 | 'default': { 81 | 'ENGINE': 'django.db.backends.sqlite3', 82 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 83 | } 84 | } 85 | 86 | 87 | # Password validation 88 | # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators 89 | 90 | AUTH_PASSWORD_VALIDATORS = [ 91 | { 92 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 93 | }, 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 96 | }, 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 99 | }, 100 | { 101 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 102 | }, 103 | ] 104 | 105 | 106 | # Internationalization 107 | # https://docs.djangoproject.com/en/2.2/topics/i18n/ 108 | 109 | LANGUAGE_CODE = 'en-us' 110 | 111 | TIME_ZONE = 'UTC' 112 | 113 | USE_I18N = True 114 | 115 | USE_L10N = True 116 | 117 | USE_TZ = True 118 | 119 | 120 | # Static files (CSS, JavaScript, Images) 121 | # https://docs.djangoproject.com/en/2.2/howto/static-files/ 122 | 123 | STATIC_URL = '/static/' 124 | STATICFILES_DIRS = [ 125 | os.path.join(BASE_DIR, "static"), 126 | ] -------------------------------------------------------------------------------- /docs/git_info/cnblogs.json: -------------------------------------------------------------------------------- 1 | {"git_sha": "d19815fa999a99b4718a952af7b6862d01ffa6c3", "file_map": {"0": "14304500", "1": "14304501", "2": "14308444", "3": "14308445", "4": "14308446", "5": "14308447", "6": "14315753", "7": "14317991", "8": "14318425", "10": "14324500", "9": "14324501", "11": "14326848", "12": "14329611"}, "imgs_map": {"0_1": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162044143-1012996894.png", "0_2": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162044415-368611460.png", "0_3": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162044602-613958255.png", "0_4": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162044834-599421181.png", "1_1": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162045283-1126750492.png", "1_2": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162045501-1244288320.png", "2_1": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162046060-1176259921.png", "2_2": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162046293-1139715677.png", "2_3": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162046491-113754611.png", "2_4": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162046690-460771974.png", "2_5": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162046858-918140395.png", "2_6": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162047115-660411673.png", "4_1": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162047808-2030749238.png", "4_2": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162047967-820842080.png", "4_3": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162048184-1130155718.png", "5_1": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210121162048603-2130542739.png", "10_1": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210125123411052-1008363908.png", "10_2": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210125123411443-1028322654.png", "10_3": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210125123411631-642420991.png", "10_4": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210125123411856-1806039300.png", "11_1": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210125184802601-1486845886.png", "11_2": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210125184802927-1332235729.png", "12_1": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210126120313695-1318533372.png", "12_2": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210126120314031-1789035137.png", "8_1": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210126120314512-1398436772.png", "8_2": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210126120314780-776472218.png", "8_3": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210126120315016-867756973.png", "8_4": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210126120315235-1907278944.png", "8_5": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210126120315432-2055413262.png", "5_2": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210128211211018-727601916.png", "5_3": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210128211211360-1006303886.png", "6_1": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210128211211744-459621385.png", "6_2": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210128211211894-1980088390.png", "7_1": "https://img2020.cnblogs.com/blog/1662891/202101/1662891-20210128223542401-264905438.png"}, "catalog": "14304389", "path": "F://UP PIG//code//SSCMS//docs//git_info\\cnblogs.json", "error": "'NoneType' object is not subscriptable", "title_map": {"0": "初步介绍与演示", "1": "项目流程梳理与数据库设计", "2": "新建项目(project)并进行设置", "3": "创建用户模型(model)", "4": "实现登录页面", "5": "实现注册功能", "6": "实现登录逻辑", "7": "修改个人信息", "8": "CSS样式优化", "10": "老师课程业务实现", "9": "创建课程模型(model)", "11": "学生课程业务实现", "12": "CSS样式完善"}} -------------------------------------------------------------------------------- /docs/3.md: -------------------------------------------------------------------------------- 1 | ## 创建用户模型(model) 2 | 3 | 4 | > 本文涉及到的新的额外知识点:`models` 5 | > 没有这部分基础的读者,建议一边阅读本文一边查阅相关知识 6 | > 这里推荐我的专栏:[Django自学笔记](https://blog.csdn.net/python1639er/article/details/105008729) 第四章内容 7 | > 数据库表的设计已在本专栏之前的博客做好了:[一、项目流程梳理与数据库设计](https://blog.csdn.net/python1639er/article/details/109614524) 8 | 9 | 这里对于用户模块,需要学生表和教师表, 10 | 那么对应的,这里需要建立两种模型:学生(Student),教师(Teacher) 11 | 模型的添加和修改要在对应`app`的`models.py`中进行 12 | 13 | 首先,打开项目下的`./user/models.py`文件 14 | 其初始内容如下: 15 | ```python 16 | from django.db import models 17 | 18 | # Create your models here. 19 | ``` 20 | 其中第一行导入了`models`类,如果你的`models.py`文件没有导入,请添加这一行 21 | 22 | 同时开始添加自己的模型代码后,最好把第三行的注释删掉 23 | 24 | ### 1 添加学生模型 25 | 在`models.py`中添加如下代码即可 26 | ```python 27 | class Student(models.Model): 28 | genders = [ 29 | ("m", "男"), 30 | ("f", "女") 31 | ] 32 | 33 | name = models.CharField(max_length=50, verbose_name="姓名") 34 | gender = models.CharField(max_length=10, choices=genders, default='m', verbose_name="性别") 35 | birthday = models.DateField(verbose_name="生日") 36 | email = models.EmailField(verbose_name="邮箱") 37 | info = models.CharField(max_length=255, verbose_name="个人简介", help_text="一句话介绍自己,不要超过250字") 38 | 39 | grade = models.CharField(max_length=4, verbose_name="年级") 40 | number = models.CharField(max_length=6, verbose_name="年级子学号") 41 | password = models.CharField(max_length=30, verbose_name="密码") 42 | 43 | class Meta: 44 | constraints = [ 45 | # 复合主键:保证 grade和number组合的student_id唯一 46 | models.UniqueConstraint(fields=['grade', 'number'], name='student_id'), 47 | ] 48 | 49 | def get_id(self): 50 | return "%s%s" % (self.grade, self.number) 51 | 52 | def __str__(self): 53 | return "%s (%s)" % (self.get_id(), self.name) 54 | ``` 55 | 56 | 说明:学生年级号为4位数字组成的字符串,年级下子学号为6位数字组成的字符串。 57 | 这两个连接起来组成学生的唯一学号,该学号也为其登录使用的账号。 58 | 59 | 比如学生李大爽,年级号为`"2020"`,子学号为`"000001"`,其学号为`"2020000001"`。 60 | 61 | ### 2 添加老师模型 62 | 在`models.py`中继续添加如下代码即可 63 | ```python 64 | class Teacher(models.Model): 65 | genders = [ 66 | ("m", "男"), 67 | ("f", "女") 68 | ] 69 | 70 | name = models.CharField(max_length=50, verbose_name="姓名") 71 | gender = models.CharField(max_length=10, choices=genders, default='m', verbose_name="性别") 72 | birthday = models.DateField(verbose_name="生日") 73 | email = models.EmailField(verbose_name="邮箱") 74 | info = models.CharField(max_length=255, verbose_name="教师简介", help_text="不要超过250字") 75 | 76 | department_no = models.CharField(max_length=3, verbose_name="院系号") 77 | number = models.CharField(max_length=7, verbose_name="院内编号") 78 | password = models.CharField(max_length=30, verbose_name="密码") 79 | 80 | class Meta: 81 | constraints = [ 82 | # 复合主键:保证 grade和number组合的student_id唯一 83 | models.UniqueConstraint(fields=['department_no', 'number'], name='teacher_id'), 84 | ] 85 | 86 | def get_id(self): 87 | return "%s%s" % (self.department_no, self.number) 88 | 89 | def __str__(self): 90 | return "%s (%s)" % (self.get_id(), self.name) 91 | ``` 92 | 93 | 说明:老师院系号为3位数字组成的字符串,院内编号为7位数字组成的字符串。 94 | 这两个连接起来组成老师的唯一教师号,该教师号也为其登录使用的账号。 95 | 96 | 比如老师牛有力,院系号为`"266"`,院内编号为`"0000001"`,其教师号为`"2660000001"`。 97 | 98 | ### 3 建立(更新)数据库 99 | 在django框架下,并不是添加好模型(model)就万事大吉了 100 | 添加好模型后,我们还需要手动执行脚本,才能根据模型生成对应的数据库表。 101 | 在项目文件夹下,打开命令行,按行依次执行: 102 | ```bash 103 | python manage.py makemigrations 104 | python manage.py migrate 105 | ``` 106 | 解释下上面这两句的作用 107 | **其中第1句会生成对应的迁移(migrations)命令。** 108 | 具体到本项目,由于是第一次运行, 109 | 那么会在`./user/migrations`文件夹下, 110 | 生成`0001_initial.py`用于记录迁移(migrations)命令 111 | 第一次运行也会在项目文件夹下,生成一个空的 database:`db.sqlite3` 112 | **第2句会执行第一句中生成的迁移(migrations)命令。** 113 | 执行完第二句,改动才真正更新到数据库文件了。 114 | 对应到本项目,就是数据库中添加了两个模型(学生、老师)对应的数据库表。 115 | > 当然,实际上,数据库中的改动并不是简单的添加了这两个模型的事,有兴趣的朋友可以打开此时的`db.sqlite3`看看。无法直接打开,需要使用对应的软件,我一般用DB Browser (SQLite),里面生成了很多个表,不过这个细说起来就复杂了。 116 | 117 | 118 | 以后我们如果修改了模型的属性之类,也要执行上面两句脚本去更新对应的数据库表。 -------------------------------------------------------------------------------- /docs/7.md: -------------------------------------------------------------------------------- 1 | ## 修改个人信息 2 | 3 | 用户模块除了注册登录之外,还需要能够修改个人的信息。 4 | 5 | ### 1 表单选择 6 | 一般来说,修改视图和注册视图可以用一样的表单。 7 | 8 | 具体到这个教务管理系统,有一个业务逻辑为: 9 | 学生注册信息可以选择年级,但是修改个人信息不能修改年级。 10 | 11 | 所以学生信息修改的表单需要修改下(在`user/forms.py`中添加如下代码) 12 | ```python 13 | class StuUpdateForm(StuRegisterForm): 14 | class Meta: 15 | model = Student 16 | fields = ('name', 17 | 'password', 18 | 'confirm_password', 19 | 'gender', 20 | 'birthday', 21 | 'email', 22 | 'info') 23 | ``` 24 | 25 | 老师信息修改的表单可以使用原来注册表单,即`TeaRegisterForm` 26 | 27 | ### 2 模板文件 28 | 明确了表单后,则可以添加对应模板文件 29 | `templates/user/update.html`如下 30 | ```html 31 | 32 | 33 | {% load static %} 34 | 35 | 36 | 37 | Register 38 | 39 | 40 | 41 | 42 | 43 |
44 |
修改个人信息
45 |
46 | {% csrf_token %} 47 | {{form.as_p}} 48 |

49 |
50 | 51 | 52 |
53 | 54 | ``` 55 | 56 | ### 3 添加视图 57 | 58 | 首先,在视图`user/cbvs.py`开头,添加导入需要的库和类 59 | ```python 60 | from django.views.generic import UpdateView 61 | from user.forms import StuUpdateForm 62 | ``` 63 | 再在最后添加如下代码 64 | ```python 65 | class UpdateStudentView(UpdateView): 66 | model = Student 67 | form_class = StuUpdateForm 68 | template_name = "user/update.html" 69 | 70 | def get_context_data(self, **kwargs): 71 | context = super(UpdateStudentView, self).get_context_data(**kwargs) 72 | context.update(kwargs) 73 | context["kind"] = "student" 74 | return context 75 | 76 | def get_success_url(self): 77 | return reverse("course", kwargs={"kind": "student"}) 78 | 79 | 80 | class UpdateTeacherView(UpdateView): 81 | model = Teacher 82 | form_class = TeaRegisterForm 83 | template_name = "user/update.html" 84 | 85 | def get_context_data(self, **kwargs): 86 | context = super(UpdateTeacherView, self).get_context_data(**kwargs) 87 | context.update(kwargs) 88 | context["kind"] = "teacher" 89 | return context 90 | 91 | def get_success_url(self): 92 | return reverse("course", kwargs={"kind": "teacher"}) 93 | 94 | ``` 95 | 在视图`user/views.py`开头,添加导入上面两个视图类`UpdateStudentView, UpdateTeacherView` 96 | 再在最后添加如下代码 97 | ```python 98 | def update(request, kind): 99 | func = None 100 | if kind == "student": 101 | func = UpdateStudentView.as_view() 102 | elif kind == "teacher": 103 | func = UpdateTeacherView.as_view() 104 | else: 105 | return HttpResponse(INVALID_KIND) 106 | 107 | pk = request.session.get("id") 108 | if pk: 109 | context = { 110 | "name": request.session.get("name", ""), 111 | "kind": request.session.get("kind", "") 112 | } 113 | return func(request, pk=pk, context=context) 114 | 115 | return redirect("login") 116 | ``` 117 | 118 | ### 4 添加路由 119 | 添加对应路由(在`user/urls.py`中的`urlpatterns`的添加) 120 | ```python 121 | path('update/', views.update, name="update"), 122 | ``` 123 | 124 | 同时再去个人主页中去添加对应的链接,使得用户可以在个人主页点击它进入信息修改页面。 125 | 126 | 这里把这个链接添加在个人主页的用户名这里, 127 | 同时出于简介美观的目的,用户名只展示一个姓(后面会给这个姓添加一个圆背景)。 128 | 129 | 修改`templates/course/nav.html`的第29行 130 | 修改前为 131 | ```html 132 | {{ info.name }} 133 | ``` 134 | 修改后为 135 | ```html 136 | 137 | {{ info.name.0 }} 138 | 139 | ``` 140 | 141 | 然后运行项目,进入个人主页后,点击用户名,可进入信息修改页面,效果如下: 142 | 143 | ![](https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/master/docs/img/7_1.png) 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /course/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | import datetime 3 | from user.models import Student, Teacher 4 | from constants import COURSE_STATUS, COURSE_OPERATION 5 | 6 | 7 | def current_year(): 8 | # refer: https://stackoverflow.com/questions/49051017/year-field-in-django/49051348 9 | return datetime.date.today().year 10 | 11 | 12 | class Course(models.Model): 13 | credits = [ 14 | (1, 1), 15 | (2, 2), 16 | (3, 3), 17 | (4, 4), 18 | (5, 5), 19 | ] 20 | semesters = [ 21 | ("Autumn", "上"), 22 | ("Spring", "下") 23 | ] 24 | name = models.CharField(max_length=50, verbose_name="课程名") 25 | introduction = models.CharField(max_length=250, verbose_name="介绍") 26 | credit = models.IntegerField(verbose_name="学分") 27 | max_number = models.IntegerField(verbose_name="课程最大人数") 28 | 29 | year = models.IntegerField(verbose_name="年份", default=current_year) 30 | semester = models.CharField(max_length=20, verbose_name="学期", choices=semesters) 31 | 32 | # 未开始选课, 1 33 | # 开始选课,未结束选课 2 34 | # 结束选课, 3 35 | # 结课 4 36 | # 已打完分 5 37 | status = models.IntegerField(verbose_name="课程状态", default=1) 38 | 39 | teacher = models.ForeignKey(Teacher, verbose_name="课程教师", on_delete=models.CASCADE) 40 | 41 | def get_status_text(self): 42 | return COURSE_STATUS[self.status] 43 | 44 | def get_op_text(self): 45 | return COURSE_OPERATION[self.status] 46 | 47 | def get_current_count(self): 48 | courses = StudentCourse.objects.filter(course=self, with_draw=False) 49 | return len(courses) 50 | 51 | def get_schedules(self): 52 | schedules = Schedule.objects.filter(course=self) 53 | return schedules 54 | 55 | def __str__(self): 56 | return "%s (%s)" % (self.name, self.teacher.name) 57 | 58 | 59 | def weekday_choices(): 60 | weekday_str = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] 61 | return [(i+1, weekday_str[i]) for i in range(7)] 62 | 63 | 64 | class Schedule(models.Model): 65 | weekday = models.IntegerField(choices=weekday_choices(), verbose_name="日期") 66 | start_time = models.TimeField(verbose_name="上课时间") 67 | end_time = models.TimeField(verbose_name="下课时间") 68 | location = models.CharField(max_length=100, verbose_name="上课地点") 69 | remarks = models.CharField(max_length=100, verbose_name="备注", null=True, blank = True) 70 | 71 | start_week = models.IntegerField(verbose_name="第几周开始") 72 | end_week = models.IntegerField(verbose_name="第几周结束") 73 | 74 | intervals = [ 75 | (1, "无间隔"), 76 | (2, "每隔一周上一次") 77 | ] 78 | week_interval = models.IntegerField(verbose_name="周间隔", choices=intervals, default=1) 79 | 80 | course = models.ForeignKey(Course, verbose_name="课程名", on_delete=models.CASCADE) 81 | 82 | def __str__(self): 83 | s = "第%s周-第%s周 " % (self.start_week, self.end_week) 84 | if self.week_interval == 2: 85 | s += "隔一周 " 86 | s += "%s %s-%s " % (self.get_weekday_display(), self.start_time.strftime("%H:%M"), 87 | self.end_time.strftime("%H:%M")) 88 | s += "在%s" % self.location 89 | if self.remarks: 90 | s += " %s" % self.remarks 91 | return s 92 | 93 | 94 | class StudentCourse(models.Model): 95 | create_time = models.DateTimeField(auto_now=True) 96 | with_draw = models.BooleanField(default=False) 97 | with_draw_time = models.DateTimeField(default=None, null=True) 98 | 99 | scores = models.IntegerField(verbose_name="成绩", null=True) 100 | comments = models.CharField(max_length=250, verbose_name="老师评价", null=True) 101 | 102 | rates = [ 103 | (1, 1), 104 | (2, 2), 105 | (3, 3), 106 | (4, 4), 107 | (5, 5), 108 | ] 109 | 110 | rating = models.IntegerField(verbose_name="学生评分", choices=rates, null=True, help_text="5分为最满意,最低分是1分") 111 | assessment = models.CharField(max_length=250, verbose_name="学生评价", null=True) 112 | 113 | student = models.ForeignKey(Student, on_delete=models.CASCADE) 114 | course = models.ForeignKey(Course, on_delete=models.CASCADE) 115 | -------------------------------------------------------------------------------- /templates/course/teacher/course.html: -------------------------------------------------------------------------------- 1 | {% extends "course/nav.html" %} 2 | {% block title %}课程详情{% endblock %} 3 | {% block content %} 4 | {% load static %} 5 | 6 |

课程详情

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
课程编号名称学分当前人数/总人数年份学期
{{ course.id }}{{ course.name }}{{ course.credit }}{{ course.get_current_count }}/{{ course.max_number }}{{ course.year }}{{ course.get_semester_display }}
30 | 31 |

上课时间

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | {% for schedule in schedules %} 42 | 43 | 44 | 45 | 49 | 50 | {% endfor %} 51 | 52 |
编号详情操作
{{ schedule.id }}{{ schedule }} 46 | 48 |
53 | 54 |

学生列表 55 | {% if course.status == 4 %} 56 | 57 | {% endif %} 58 |

59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | {% for cs in course_students %} 72 | 73 | 74 | 75 | 76 | 80 | 84 | 97 | 98 | {% endfor %} 99 | 100 |
学生学号学生姓名学生邮箱得分评价操作
{{ cs.student.get_id}}{{ cs.student.name }}{{ cs.student.email }} 77 | {% if cs.scores == None %}-{% endif %} 78 | {% if cs.scores != None %}{{ cs.scores }}{% endif %} 79 | 81 | {% if cs.scores == None %}-{% endif %} 82 | {% if cs.scores != None %}{{ cs.comments }}{% endif %} 83 | 85 | {% if course.status == 4 %} 86 | {% if cs.scores == None %} 87 | 89 | {% else %} 90 | 92 | {% endif %} 93 | {% else %} 94 | - 95 | {% endif %} 96 |
101 | 102 | {% if course.status == 5 %} 103 |

学生评价

104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | {% for cs in sorted_course_students %} 113 | {% if cs.rating != None %} 114 | 115 | 116 | 117 | 118 | {% endif %} 119 | {% endfor %} 120 | 121 |
学生评分学生评价
{{ cs.rating }}{{ cs.assessment }}
122 | {% endif %} 123 | {% endblock %} -------------------------------------------------------------------------------- /docs/9.md: -------------------------------------------------------------------------------- 1 | ## 创建课程模型(model) 2 | 3 | 对于课程模块,需要: 4 | - 课程表 5 | - 学生课程表:学生和课程的关系表 6 | - 时刻表:课程上课时间设置 7 | 8 | 由于课程会有多个状态,这个可以先在`constants.py`中去记录好(即添加代码如下) 9 | ```python 10 | COURSE_STATUS = { 11 | 1: "未开始选课", 12 | 2: "开始选课", 13 | 3: "结束选课", 14 | 4: "结课", 15 | 5: "打分完成", 16 | } 17 | 18 | COURSE_OPERATION = { 19 | 1: "开始选课", 20 | 2: "结束选课", 21 | 3: "结课", 22 | 4: "给分", 23 | 5: "查看详情" 24 | } 25 | ``` 26 | 27 | 然后在`course/models.py`中导入下面会需要的所有库 28 | ```python 29 | from django.db import models 30 | import datetime 31 | from user.models import Student, Teacher 32 | from constants import COURSE_STATUS, COURSE_OPERATION 33 | ``` 34 | 35 | ### 1 添加课程模型 36 | 在`course/models.py`中添加代码如下 37 | 38 | ```python 39 | def current_year(): 40 | # refer: https://stackoverflow.com/questions/49051017/year-field-in-django/49051348 41 | return datetime.date.today().year 42 | 43 | class Course(models.Model): 44 | credits = [ 45 | (1, 1), 46 | (2, 2), 47 | (3, 3), 48 | (4, 4), 49 | (5, 5), 50 | ] 51 | semesters = [ 52 | ("Autumn", "上"), 53 | ("Spring", "下") 54 | ] 55 | name = models.CharField(max_length=50, verbose_name="课程名") 56 | introduction = models.CharField(max_length=250, verbose_name="介绍") 57 | credit = models.IntegerField(verbose_name="学分") 58 | max_number = models.IntegerField(verbose_name="课程最大人数") 59 | 60 | year = models.IntegerField(verbose_name="年份", default=current_year) 61 | semester = models.CharField(max_length=20, verbose_name="学期", choices=semesters) 62 | 63 | # 未开始选课, 1 64 | # 开始选课,未结束选课 2 65 | # 结束选课, 3 66 | # 结课 4 67 | # 已打完分 5 68 | status = models.IntegerField(verbose_name="课程状态", default=1) 69 | 70 | teacher = models.ForeignKey(Teacher, verbose_name="课程教师", on_delete=models.CASCADE) 71 | 72 | def get_status_text(self): 73 | return COURSE_STATUS[self.status] 74 | 75 | def get_op_text(self): 76 | return COURSE_OPERATION[self.status] 77 | 78 | def get_current_count(self): 79 | courses = StudentCourse.objects.filter(course=self, with_draw=False) 80 | return len(courses) 81 | 82 | def get_schedules(self): 83 | schedules = Schedule.objects.filter(course=self) 84 | return schedules 85 | 86 | def __str__(self): 87 | return "%s (%s)" % (self.name, self.teacher.name) 88 | ``` 89 | 90 | 补充说明: `get_status_text`和`get_op_text`方法是为了方便在模板中调用。 91 | 92 | 93 | ### 2 添加课程时刻表模型 94 | 在`course/models.py`中添加代码如下 95 | ```python 96 | def weekday_choices(): 97 | weekday_str = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] 98 | return [(i+1, weekday_str[i]) for i in range(7)] 99 | 100 | 101 | class Schedule(models.Model): 102 | weekday = models.IntegerField(choices=weekday_choices(), verbose_name="日期") 103 | start_time = models.TimeField(verbose_name="上课时间") 104 | end_time = models.TimeField(verbose_name="下课时间") 105 | location = models.CharField(max_length=100, verbose_name="上课地点") 106 | remarks = models.CharField(max_length=100, verbose_name="备注", null=True, blank = True) 107 | 108 | start_week = models.IntegerField(verbose_name="第几周开始") 109 | end_week = models.IntegerField(verbose_name="第几周结束") 110 | 111 | intervals = [ 112 | (1, "无间隔"), 113 | (2, "每隔一周上一次") 114 | ] 115 | week_interval = models.IntegerField(verbose_name="周间隔", choices=intervals, default=1) 116 | 117 | course = models.ForeignKey(Course, verbose_name="课程名", on_delete=models.CASCADE) 118 | 119 | def __str__(self): 120 | s = "第%s周-第%s周 " % (self.start_week, self.end_week) 121 | if self.week_interval == 2: 122 | s += "隔一周 " 123 | s += "%s %s-%s " % (self.get_weekday_display(), self.start_time.strftime("%H:%M"), 124 | self.end_time.strftime("%H:%M")) 125 | s += "在%s" % self.location 126 | if self.remarks: 127 | s += " %s" % self.remarks 128 | return s 129 | ``` 130 | 131 | ### 3 添加学生课程关系表模型 132 | 133 | 在`course/models.py`中添加代码如下 134 | 135 | ```python 136 | class StudentCourse(models.Model): 137 | create_time = models.DateTimeField(auto_now=True) 138 | with_draw = models.BooleanField(default=False) 139 | with_draw_time = models.DateTimeField(default=None, null=True) 140 | 141 | scores = models.IntegerField(verbose_name="成绩", null=True) 142 | comments = models.CharField(max_length=250, verbose_name="老师评价", null=True) 143 | 144 | rates = [ 145 | (1, 1), 146 | (2, 2), 147 | (3, 3), 148 | (4, 4), 149 | (5, 5), 150 | ] 151 | 152 | rating = models.IntegerField(verbose_name="学生评分", choices=rates, null=True, help_text="5分为最满意,最低分是1分") 153 | assessment = models.CharField(max_length=250, verbose_name="学生评价", null=True) 154 | 155 | student = models.ForeignKey(Student, on_delete=models.CASCADE) 156 | course = models.ForeignKey(Course, on_delete=models.CASCADE) 157 | ``` 158 | 159 | ### 4 建立(更新)数据库 160 | 161 | 同[第三节第三部分](https://github.com/BigShuang/SimpleStudentCourseManagementSystem/blob/master/docs/3.md) 162 | 163 | 简单复述一遍,即在项目文件夹下打开命令行窗口,执行: 164 | ```shell 165 | python manage.py makemigrations 166 | python manage.py migrate 167 | ``` -------------------------------------------------------------------------------- /docs/2.md: -------------------------------------------------------------------------------- 1 | ## 新建项目(project)并进行设置 2 | 3 | ### 0 - 版本说明 4 | - python: 3.6.5 5 | - Django: 2.2.11 6 | 7 | 安装或更新见本人博客[Django自学笔记0-2 框架版本与相关工具](https://blog.csdn.net/python1639er/article/details/105010357) 8 | 9 | ### 1 - 新建项目 10 | > 关于新建项目,我之前的博客有详细写过: 11 | [Django自学笔记 1-1 新建项目](https://blog.csdn.net/python1639er/article/details/105026720) 12 | 所以这里简单展示新建过程和结果。 13 | 14 | - 通过命令行新建本项目(本项目名为 SSCMS) 15 | 16 | ``` 17 | django-admin startproject SSCMS 18 | ``` 19 | 运行后项目文件夹结构如下 20 | ```txt 21 | SSCMS 22 | |--manage.py 23 | |--SSCMS 24 | |--settings.py 25 | |--urls.py 26 | |--wsgi.py 27 | |--__init__.py 28 | ``` 29 | - 使用Pycharm新建本项目 30 | 31 | File->New Project后,左侧栏选择Django,Location设置为自己的项目文件夹(没有会自动新建),其他不动,截图如下 32 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201214102227705.png) 33 | 34 | 点击create就好了,此时项目文件夹结构如下 35 | ```txt 36 | SSCMS 37 | |--manage.py 38 | |--SSCMS 39 | | |--settings.py 40 | | |--urls.py 41 | | |--wsgi.py 42 | | |--__init__.py 43 | |--templates 44 | ``` 45 | 这里和上方命令行新建的项目文件夹结构中相比,多了一个templates文件夹, 46 | 没关系,没有 templates 文件夹新建一个templates文件夹就好。 47 | 48 | 这里简单说一下,templates文件夹是用来存放模板template的。 49 | 模板template具体后面会说。 50 | 51 | ### 2 - 设置 52 | **理论上在1中会自动生成设置好了的设置文件:`./SSCMS/settings.py`** 53 | 54 | 但是由于版本不同或者一些其他的原因,可能会存在设置文件不对(没设置完)的情况。 55 | 56 | 此时需要手动校对下`./SSCMS/settings.py`, 主要是检查设置下这几个 57 | - INSTALLED_APPS 58 | ``` 59 | INSTALLED_APPS = [ 60 | 'django.contrib.admin', 61 | 'django.contrib.auth', 62 | 'django.contrib.contenttypes', 63 | 'django.contrib.sessions', 64 | 'django.contrib.messages', 65 | 'django.contrib.staticfiles', 66 | ] 67 | ``` 68 | - MIDDLEWARE 69 | ``` 70 | MIDDLEWARE = [ 71 | 'django.middleware.security.SecurityMiddleware', 72 | 'django.contrib.sessions.middleware.SessionMiddleware', 73 | 'django.middleware.common.CommonMiddleware', 74 | 'django.middleware.csrf.CsrfViewMiddleware', 75 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 76 | 'django.contrib.messages.middleware.MessageMiddleware', 77 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 78 | ] 79 | ``` 80 | - TEMPLATES 81 | ``` 82 | TEMPLATES = [ 83 | { 84 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 85 | 'DIRS': [os.path.join(BASE_DIR, 'templates')] 86 | , 87 | 'APP_DIRS': True, 88 | 'OPTIONS': { 89 | 'context_processors': [ 90 | 'django.template.context_processors.debug', 91 | 'django.template.context_processors.request', 92 | 'django.contrib.auth.context_processors.auth', 93 | 'django.contrib.messages.context_processors.messages', 94 | ], 95 | }, 96 | }, 97 | ] 98 | ``` 99 | - DATABASES 100 | ``` 101 | DATABASES = { 102 | 'default': { 103 | 'ENGINE': 'django.db.backends.sqlite3', 104 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 105 | } 106 | } 107 | ``` 108 | 109 | 同时后续我们也需要添加一些我们自己的设置,这个后面遇到再说 110 | 111 | ### 3 - 新建应用(app) - user、course 112 | > 对于新手而言,往往会困惑应用(app)是什么? 113 | 114 | > 这里粗略的解释下,对于一个项目(project)而言,应用(app)可以理解为模块、子系统,用于承载一个项目中相对独立的一块功能。 115 | 116 | > 就像一个房子一般要由多个房间(门厅,餐厅,客厅,卧室,厨房,卫生间等)组成,一个项目往往由多个应用组成。 117 | 比如本项目中,将教务管理系统分为用户、课程模块。 118 | 119 | > 对于小的项目来说,只有一个应用也是可以的,但是对于大一点的项目,推荐将功能先划分为不同的模块,否则就像一个房子只用一个房间承载餐厅客厅卧室厨房卫生间的功能。。。 120 | 121 | 通过命令行新建应用(app) 122 | ```shell 123 | py manage.py startapp app_name 124 | ``` 125 | 126 | 教务管理系统中,我们首先需要用户模块,用户模块名为`user`, 那么命令行如下 127 | ```shell 128 | py manage.py startapp user 129 | ``` 130 | 如果系统中同时安装有python2和python3,则需要指定python版本为3 131 | 此时命令如下 132 | ``` 133 | py3 manage.py startapp user 134 | ``` 135 | 136 | 新建应用(app)后,要去 `setting.py`中的`INSTALLED_APPS`添加应用`user` 137 | `INSTALLED_APPS`添加后如下 138 | ``` 139 | INSTALLED_APPS = [ 140 | 'django.contrib.admin', 141 | 'django.contrib.auth', 142 | 'django.contrib.contenttypes', 143 | 'django.contrib.sessions', 144 | 'django.contrib.messages', 145 | 'django.contrib.staticfiles', 146 | 'user', 147 | ] 148 | ``` 149 | 150 | 建好应用(app)后项目文件夹如下 151 | ```txt 152 | SSCMS 153 | |--manage.py 154 | |--SSCMS 155 | | |--settings.py 156 | | |--urls.py 157 | | |--wsgi.py 158 | | |--__init__.py 159 | |--templates 160 | |--user 161 | |--admin.py 162 | |--apps.py 163 | |--migrations 164 | | |--__init__.py 165 | |--models.py 166 | |--tests.py 167 | |--views.py 168 | |--__init__.py 169 | ``` 170 | 171 | 172 | 然后同理,通过命令行新建应用:course,代表课程模块 173 | ```shell 174 | py manage.py startapp course 175 | ``` 176 | 去 `setting.py`中的`INSTALLED_APPS`添加应用`course` 177 | 178 | ### 4 - 建立相关文件夹 179 | 在本文第一部分,简单介绍过了用于存放模板的`templates`文件夹。 180 | 181 | 除了这个,我们还需要建立用于存放静态文件的`static`文件夹, 182 | 直接用电脑在项目文件夹下右键新建文件夹并命名为`static`即可 183 | 184 | 然后项目文件夹如下 185 | ``` 186 | SSCMS 187 | |--manage.py 188 | |--SSCMS 189 | |--settings.py 190 | |--urls.py 191 | |--wsgi.py 192 | |--__init__.py 193 | |--static 194 | |--templates 195 | |--user 196 | |--admin.py 197 | |--apps.py 198 | |--migrations 199 | |--__init__.py 200 | |--models.py 201 | |--tests.py 202 | |--views.py 203 | |--__init__.py 204 | |--course 205 | |--admin.py 206 | |--apps.py 207 | |--migrations 208 | |--__init__.py 209 | |--models.py 210 | |--tests.py 211 | |--views.py 212 | |--__init__.py 213 | ``` 214 | 215 | 216 | ### 5 - 运行项目 217 | - **A、通过Pycharm来运行(推荐)** 218 | 219 | Pycharm专业版会自动为Django项目添加一个可以运行的服务器配置 220 | 221 | 直接点击这里就可以运行了 222 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200322152345102.png) 223 | 224 | 对于什么都没有的新项目,运行后访问 225 | ```txt 226 | http://127.0.0.1:8000/ 227 | ``` 228 | 将如下图 229 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200322153752822.png) 230 | 231 | 运行后,点击这个红色的方块,即可关闭服务器。 232 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200322153603826.png) 233 | 234 | - **B、命令行运行服务器(不推荐)** 235 | 在项目文件夹中打开命令行并执行 236 | ``` 237 | py manage.py runserver 238 | ``` 239 | 在命令行中,按Ctrl+C可关闭服务器 240 | 241 | - **C、 Pycharm未自动添加一个可以运行的服务器配置** 242 | 243 | 如果你是Pycharm社区版(免费版),可能Pycharm不会自动添加一个可以运行的服务器配置。 244 | 245 | 那么你需要手动添加一个服务器配置 246 | 247 | 在pycharm右上角这里点击下拉后出现的Edit Configurations 248 | ![](https://img-blog.csdnimg.cn/20201214113543326.png) 249 | 250 | 在弹出的窗口中按照以下步骤操作 251 | ![](https://img-blog.csdnimg.cn/20201214114043801.png) 252 | 其中, 253 | 3中的name可以自己定义, 254 | 4中的文件要设置成自己项目下的`manage.py`文件 255 | 256 | 操作完成后点击窗体右下角的Apply按钮就好。 257 | 就可以像本部分A(第五部分通过Pycharm来运行)中那样运行这个项目了 258 | 259 | -------------------------------------------------------------------------------- /docs/4.md: -------------------------------------------------------------------------------- 1 | ## 实现登录页面 2 | 3 | > 本文涉及到的新的额外知识点:`template`、`view`、`urls`、`forms` 4 | > 没有这部分基础的读者,建议一边阅读本文一边查阅相关知识 5 | > 这里推荐我的专栏:[Django自学笔记](https://blog.csdn.net/python1639er/article/details/105008729) 相关章节内容 6 | 7 | 8 | ### 0 添加用户视图(view)与模板(template)、设置urls 9 | > Django 采用了 MVT 的软件设计模式,其中MVT分别指 10 | - Model(模型) - 负责数据 11 | - View(视图)- 负责逻辑 12 | - Template(模板)- 负责前端展示 13 | 14 | 我们在前面已经实现了模型(model) 15 | 16 | 这里要实现一个页面,基本套路是 17 | 1. 先完成模板(template) - 前端展示用 18 | 2. 然后实现视图(view) - 后端逻辑 19 | 3. 设置好url - 设置url和view的对应关系 20 | 21 | 本文就是按照这个顺序来组织代码的 22 | 23 | 24 | ### 1 主页面 25 | 本项目主页面实际是一个选择身份的页面,点击后跳转到具体的身份登录页,整个页面实现起来比较简单 26 | 27 | - A 首先添加模板(template) 28 | 这里由于主页面和登录页面的背景是相同的,所以这两个页面可以使用共同的母版,这里先添加母版 29 | 30 | 在项目的`template`文件夹下,新建user文件夹,在其中添加`background.html`,内容 31 | ```html 32 | 33 | 34 | 35 | 36 | 37 | 学生选课管理系统 38 | 39 | 40 | 41 |
42 |
43 |
学生选课管理系统
44 |
Student Course Management System
45 | {% block welcome_message %} 46 | {% endblock %} 47 |
48 | 52 |
53 | 54 | 55 | ``` 56 | 然后再添加`login_home.html`文件,内容如下 57 | ```html 58 | {% extends "user/background.html" %} 59 | {% block login_container %} 60 | 63 | 66 | {% endblock %} 67 | ``` 68 | - B 添加对应的视图(view)方法 69 | 在`./user/views.py`中,添加方法 70 | ```python 71 | def home(request): 72 | return render(request, "user/login_home.html") 73 | ``` 74 | - C 设置主页的url 75 | 76 | 新建`./user/urls.py`代码如下 77 | ```python 78 | from user import views 79 | from django.urls import path 80 | 81 | urlpatterns = [ 82 | path('login/', views.home, name="login"), 83 | ] 84 | ``` 85 | 在`./SSCMS/urls.py`中添加user模块的urls, 86 | 修改后的`./SSCMS/urls.py`代码如下(原来的注释建议保留,不保留也无所谓) 87 | 88 | ```python 89 | from django.urls import path, include 90 | 91 | urlpatterns = [ 92 | path('user/', include("user.urls")), 93 | ] 94 | ``` 95 | 96 | 97 | 此时运行项目,访问[http://127.0.0.1:8000/user/login/](http://127.0.0.1:8000/user/login/) 98 | 效果如下 99 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201215170054729.png) 100 | 101 | ### 2 登录页面view实现 102 | 103 | 登录需要表单来提交登录信息(账号和密码),这里我们使用Django From 104 | 在`user`文件夹中新建`forms.py`, 添加以下代码来实现老师和学生的登录信息表单 105 | ```python 106 | from django import forms 107 | from user.models import Student, Teacher 108 | 109 | 110 | class StuLoginForm(forms.Form): 111 | uid = forms.CharField(label='学号', max_length=10) 112 | password = forms.CharField(label='密码', max_length=30, widget=forms.PasswordInput) 113 | 114 | 115 | class TeaLoginForm(forms.Form): 116 | uid = forms.CharField(label='教职工号', max_length=10) 117 | password = forms.CharField(label='密码', max_length=30, widget=forms.PasswordInput) 118 | ``` 119 | 120 | - A 添加模板(template) 121 | 在项目的`template/user`文件夹下,添加`login_detail.html`: 122 | ```html 123 | {% extends "user/background.html" %} 124 | {% block welcome_message %} 125 |
欢迎
126 | {% endblock %} 127 | {% block login_container %} 128 | {% if kind == "student" %} 129 | 130 | {% else %} 131 | 132 | {% endif %} 133 |
134 |
135 | {% csrf_token %} 136 | {{form.as_p}} 137 |
138 | 139 | 注册 140 |
141 |
142 | 143 |
144 | {% endblock %} 145 | ``` 146 | 147 | - B 添加对应的视图(view)方法 148 | 为了区分学生和老师,这里给该视图添加了`kind`参数 149 | `kind`必须为`"teacher"`或`"student"`,如果不是的话,就会返回一个文本标明这不是一个合适的kind。 150 | 所以需要在视图中添加用于返回的文本,考虑到后续也会有一些其他文本。 151 | 这里建议把文本放在一个专门的py文件中,不仅方便修改和检查,在以后如果要支持多语言也方便。 152 | 153 | 这里在项目下建立一个`constants.py`文件,存放相关的文本 154 | 先添加一个标明这是不支持的`kind`的文本 155 | ```python 156 | INVALID_KIND = "Invalid kind.kind should be student or teacher." 157 | ``` 158 | 159 | 在`./user/views.py`中,添加方法 160 | ```python 161 | from django.http.response import HttpResponse 162 | 163 | from constants import INVALID_KIND 164 | from user.forms import StuLoginForm, TeaLoginForm 165 | 166 | def login(request, *args, **kwargs): 167 | if not kwargs or kwargs.get("kind", "") not in ["student", "teacher"]: 168 | return HttpResponse(INVALID_KIND) 169 | 170 | kind = kwargs["kind"] 171 | context = {'kind': kind} 172 | 173 | if request.method == 'POST': 174 | if kind == "teacher": 175 | form = TeaLoginForm(data=request.POST) 176 | else: 177 | form = StuLoginForm(data=request.POST) 178 | 179 | if form.is_valid(): 180 | uid = form.cleaned_data["uid"] 181 | 182 | temp_res = "hello, %s" % uid 183 | return HttpResponse(temp_res) 184 | else: 185 | context['form'] = form 186 | else: 187 | if kind == "teacher": 188 | form = TeaLoginForm() 189 | else: 190 | form = StuLoginForm() 191 | 192 | context['form'] = form 193 | 194 | return render(request, 'user/login_detail.html', context) 195 | ``` 196 | 197 | 由于还没有实现注册功能,所以这里的登录只先实现了各空壳, 198 | 填写账号密码就先直接各个hello的相应 199 | 200 | - C 设置主页的url 201 | 在`./user/urls.py`中,给`urlpatterns`列表添加元素 202 | ```python 203 | path('login/', views.login, name="login") 204 | ``` 205 | 206 | 然后这个时候我们需要去更新下前面主页的href,指向对应的学生老师登录页 207 | 更新后的`login_home.html`文件如下 208 | ```html 209 | {% extends "user/background.html" %} 210 | {% block login_container %} 211 | 214 | 217 | {% endblock %} 218 | ``` 219 | 220 | 此时运行项目,访问[http://127.0.0.1:8000/user/login/](http://127.0.0.1:8000/user/login/) 221 | 点击我是学生,对应页面效果: 222 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201215174909468.png) 223 | 随便填个学号密码(比如12345678) 224 | 点击登录后页面: 225 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201215175038300.png) 226 | 227 | 228 | 到这里,一个简陋的登录页面就算完成了 -------------------------------------------------------------------------------- /templates/course/student/home.html: -------------------------------------------------------------------------------- 1 | {% extends "course/nav.html" %} 2 | {% block title %}HomePage{% endblock %} 3 | {% block content %} 4 | {% load static %} 5 | 6 |
7 |
8 |
9 | {% csrf_token %} 10 | 11 | 12 |
13 | 14 | {% if view_kind != "select"%} 15 | 16 | {% endif %} 17 | {% if view_kind != "withdraw"%} 18 | 19 | {% endif %} 20 | {% if view_kind != "is_end"%} 21 | 22 | {% endif %} 23 | {% if view_kind != "current"%} 24 | 25 | {% endif %} 26 |
27 |

{% if view_kind == "select"%} 28 | 选课 29 | {% elif view_kind == "withdraw"%} 30 | 撤课 31 | {% elif view_kind == "is_end"%} 32 | 查看结课课程 33 | {% elif view_kind == "current"%} 34 | 查看当前课程 35 | {% endif %}

36 | 37 | 38 | 39 | 40 | 41 | 42 | {% if view_kind == "is_end" %} 43 | 44 | {% else %} 45 | 46 | 47 | 48 | {% endif %} 49 | 50 | {% if view_kind == "is_end" %} 51 | 52 | 53 | 54 | 55 | 56 | {% else %} 57 | 58 | 59 | {% endif %} 60 | 61 | 62 | 63 | {% if view_kind == "is_end" %} 64 | {# end course show student course #} 65 | {% for sc in course_list %} 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | {% if sc.rating == None %} 75 | 76 | 77 | 82 | {% else %} 83 | 84 | 85 | 88 | {% endif %} 89 | 90 | {% endfor %} 91 | {% else %} 92 | {% for course in course_list %} 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 106 | 119 | 120 | {% endfor %} 121 | {% endif %} 122 | 123 | 124 |
课程编号名称学分年份学期当前人数/
最大人数
年份学期教师得分评语学生评分学生评价操作上课时间操作
{{ 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 }}-- 78 | 79 | 81 | {{ sc.rating }}{{ sc.assessment }} 86 | 87 |
{{ course.id }}{{ course.name }}{{ course.credit }}{{ course.get_current_count }}/{{ course.max_number }}{{ course.year }}{{ course.get_semester_display }}{{ course.teacher.name }} 102 | {% for schedule in course.get_schedules %} 103 |
{{ schedule }}
104 | {% endfor %} 105 |
107 | {% if view_kind == "select"%} 108 | 110 | {% endif %} 111 | {% if view_kind == "withdraw"%} 112 | 114 | {% endif %} 115 | {% if view_kind == "current"%} 116 | - 117 | {% endif %} 118 |
125 |
126 | {% endblock %} -------------------------------------------------------------------------------- /docs/6.md: -------------------------------------------------------------------------------- 1 | ## 实现登录逻辑 2 | 3 | 4 | ### 1 业务逻辑 5 | 6 | > 本教程第四节里面实现了登录页面和一个空的登录逻辑。 7 | > 第六节这里就把登录逻辑补全。 8 | 9 | 登录的业务逻辑是: 10 | - 检查是否注册 11 | - 未注册,则提示账号不存在 12 | - 注册但密码不匹配,提示密码不正确 13 | - 注册且信息匹配,成功登录,跳转到个人主页,同时通过cookie保存登录信息。 14 | 15 | 对于教务管理系统,个人主页应该是其课程主页,所以本部分还需要添加课程主页,这里先只实现一个空的展示其个人信息的课程主页。 16 | 17 | ### 2 修改登录视图 18 | 修改`user/views.py`中的`login`方法如下: 19 | ```python 20 | def login(request, kind): 21 | if kind not in ["teacher", "student"]: 22 | return HttpResponse(INVALID_KIND) 23 | 24 | if request.method == 'POST': 25 | if kind == "teacher": 26 | form = TeaLoginForm(data=request.POST) 27 | else: 28 | form = StuLoginForm(data=request.POST) 29 | 30 | if form.is_valid(): 31 | uid = form.cleaned_data["uid"] 32 | if len(uid) != 10: 33 | form.add_error("uid", "账号长度必须为10") 34 | else: 35 | if kind == "teacher": 36 | department_no = uid[:3] 37 | number = uid[3:] 38 | object_set = Teacher.objects.filter(department_no=department_no, number=number) 39 | else: 40 | grade = uid[:4] 41 | number = uid[4:] 42 | object_set = Student.objects.filter(grade=grade, number=number) 43 | if object_set.count() == 0: 44 | form.add_error("uid", "该账号不存在.") 45 | else: 46 | user = object_set[0] 47 | if form.cleaned_data["password"] != user.password: 48 | form.add_error("password", "密码不正确.") 49 | else: 50 | request.session['kind'] = kind 51 | request.session['user'] = uid 52 | request.session['id'] = user.id 53 | 54 | return redirect("course", kind=kind) 55 | 56 | return render(request, 'user/login_detail.html', {'form': form, 'kind': kind}) 57 | else: 58 | context = {'kind': kind} 59 | if request.GET.get('uid'): 60 | uid = request.GET.get('uid') 61 | context['uid'] = uid 62 | if kind == "teacher": 63 | form = TeaLoginForm({"uid": uid, 'password': '12345678'}) 64 | else: 65 | form = StuLoginForm({"uid": uid, 'password': '12345678'}) 66 | else: 67 | if kind == "teacher": 68 | form = TeaLoginForm() 69 | else: 70 | form = StuLoginForm() 71 | context['form'] = form 72 | if request.GET.get('from_url'): 73 | context['from_url'] = request.GET.get('from_url') 74 | 75 | return render(request, 'user/login_detail.html', context) 76 | ``` 77 | 78 | 登录后会在cookie中存储以下信息: 79 | - `kind` : 用户类型,学生或老师 80 | - `user` : 用户学号或教师编号 81 | 82 | ### 3 添加简单主页 83 | 84 | 目前虽然是个人主页,但是后面将会是课程主页,老师的和学生的必然是不同的。 85 | 所以我们现在也先提前规划好,将主页分为学生的和老师的来处理。 86 | 87 | `course/views.py`代码如下 88 | ```python 89 | from django.http.response import HttpResponse 90 | from django.shortcuts import render, reverse, redirect 91 | 92 | from user.models import Student, Teacher 93 | from constants import INVALID_KIND 94 | 95 | 96 | def get_user(request, kind): 97 | """ 98 | 99 | :param request: 100 | :param kind: teacher or student 101 | :return: return Teacher instance or Student instance 102 | """ 103 | if request.session.get('kind', '') != kind or kind not in ["student", "teacher"]: 104 | return None 105 | 106 | if len(request.session.get('user', '')) != 10: 107 | return None 108 | 109 | uid = request.session.get('user') 110 | if kind == "student": 111 | # 找到对应学生 112 | grade = uid[:4] 113 | number = uid[4:] 114 | student_set = Student.objects.filter(grade=grade, number=number) 115 | if student_set.count() == 0: 116 | return None 117 | return student_set[0] 118 | else: 119 | # 找到对应老师 120 | department_no = uid[:3] 121 | number = uid[3:] 122 | teacher_set = Teacher.objects.filter(department_no=department_no, number=number) 123 | if teacher_set.count() == 0: 124 | return None 125 | return teacher_set[0] 126 | 127 | 128 | # Create your views here. 129 | def home(request, kind): 130 | if kind == "teacher": 131 | return teacher_home(request) 132 | elif kind == "student": 133 | return student_home(request) 134 | return HttpResponse(INVALID_KIND) 135 | 136 | 137 | def teacher_home(request): 138 | kind = "teacher" 139 | user = get_user(request, kind) 140 | 141 | if not user: 142 | return redirect('login', kind=kind) 143 | 144 | info = { 145 | "name": user.name, 146 | "kind": kind 147 | } 148 | 149 | context = { 150 | "info": info 151 | } 152 | 153 | return render(request, 'course/nav.html', context) 154 | 155 | def student_home(request): 156 | kind = "student" 157 | user = get_user(request, kind) 158 | 159 | if not user: 160 | return redirect('login', kind = kind) 161 | 162 | info = { 163 | "name": user.name, 164 | "kind": kind 165 | } 166 | 167 | context = { 168 | "info": info 169 | } 170 | 171 | return render(request, 'course/nav.html', context) 172 | ``` 173 | 174 | 注意,跳转到一个带参数的url,有两种写法 175 | - `return redirect(reverse("login", kwargs={"kind": kind}))` 176 | - `return redirect('login', kind = kind)` 177 | 178 | 这两种写法返回效果一样 179 | 180 | 添加模板文件`templates/course/nav.html` 181 | 182 | ```html 183 | 184 | 185 | {% load static %} 186 | 187 | 188 | 189 | {% block title %}{% endblock %} 190 | 191 | 192 | 193 | 212 | 213 |
214 | {% block content %}{% endblock %} 215 |
216 | 217 | 218 | 219 | 220 | ``` 221 | 222 | 然后添加`course/urls.py`如下 223 | ```python 224 | from django.urls import path 225 | from course.views import * 226 | 227 | urlpatterns = [ 228 | path('/', home, name="course"), 229 | ] 230 | ``` 231 | 232 | 同时还要去改下主url,即在`SSCMS/urls.py`的`urlpatterns`中添加 233 | ```python 234 | path('course/', include("course.urls")), 235 | ``` 236 | 237 | 此时运行软件,登录账号后(这里又注册了一个叫李大爽的用户), 238 | 结果如图 239 | ![](https://raw.githubusercontent.com/BigShuang/SimpleStudentCourseManagementSystem/master/docs/img/6_1.png) 240 | 241 | ### 4 实现退出登录 242 | 退出登录的视图方法如下(在`user/views.py`中添加) 243 | ```python 244 | def logout(request): 245 | if request.session.get("kind", ""): 246 | del request.session["kind"] 247 | if request.session.get("user", ""): 248 | del request.session["user"] 249 | if request.session.get("id", ""): 250 | del request.session["id"] 251 | return redirect(reverse("login")) 252 | ``` 253 | 添加对应路由(在`user/urls.py`中的`urlpatterns`的添加) 254 | ```python 255 | path('logout/', views.logout, name="logout") 256 | ``` 257 | 258 | 在修改下登录后的主页视图(即`templates/course/nav.html`) 259 | 在其中的第24行处(即`