引入:传统的excel统计管理学员信息的方式已经没法知足日渐增加的业务需求,所以公司急需一套方便易用的“学员管理系统”,来提升公司在校学员信息的收集和处理能力,鉴于python学院全栈x期的学员都聪明伶俐可爱至极,所以公司领导通过慎重考虑决定将此项目交给你们开发。html
本次学员高经理系统设计开发,时间紧任务重,但愿各位开发人员可以尽快完成项目的开发。前端
具体需求为:python
学员管理系统有三个角色:学生,老师和班级。mysql
学生有姓名和所属班级。sql
班级信息有:班级名称。数据库
老师信息有:姓名,老师能够带多个班级,每一个班级不一样阶段也会有不一样的老师讲课。后端
抓紧时间,越快越好浏览器
--建立班级表 create table class( id int auto_increment primary key, name char(10) not null )engine = innodb DEFAULT charset = utf8; --建立学生表 create table student( id int auto_increment primary key, name char(10) not null, class_id int, foreign key (class_id) references class(id) on delete cascade on update cascade )engine = innodb default charset = utf8; --建立老师表 create table teacher( id int auto_increment primary key, name char(10) not null )engine = innodb default charset = utf8; --建立老师和班级关系表 create table teacher2class( id int auto_increment primary key, teacher_id int, foreign key (teacher_id) references teacher(id) on delete cascade on update cascade, class_id int, foreign key(class_id) references class(id) no delete cascade on update cascade )engine = innodb default charset=utf8;
使用Bootstrap搭建的管理后台页面数据结构
视图函数+模板语言+pymysqlapp
咱们把全部的处理请求相关的函数从urls.py中拿出来,同一放在一个叫views.py的文件中,这样就把代码模块化,实现不一样的功能的代码在不一样的模块。
urls.py文件中只放置URL和函数的对应关系
views.py文件中只放置哪些针对不一样URl执行的函数。
这个时候就须要在urls.py中导入views.py文件(views前面加 . 表名从当前目录下导入)
from .views import index , login , class_list , delete_class , add_class , edit_class
def calss_list(request): #这个函数是展现全部的班级列表 #1,去数据库里取数据 #链接数据库 coon = pymysql.connect(host='localhost', user='root', password='123456', database='数据库名', charset=‘utf8’) #指定pymysql查询出的每条数据的类型是字典类型({‘字段名’:‘值’})**** cursor = conn.cursor(cursor=pymysql.cursor.DictCursor) #写好查询的SQL语句 sql = 'select id, cname from class order by id;' #执行上面定义好的sql语句 cursor.execute(sql) #拿到全部查询到的结果 #此处查询结果是一个大列表,里面是一个个字典 ret = cursor.fetchall() #关闭链接 cursor.close() conn.close() #2,用数据去替换HTML页面中的特殊符号,特殊符号是按照Django模板语言的语法写好的 #render()的第三个参数是一个字典,字典的key是什么,HTML文件中的变量就写什么 return render(request, 'class_list.html', {'class_list':ret})
这里用到了Django模板语言的for循环,模板语言部份内容详见页面下方
<!--Table --> <div class = 'my-table-wrapper'> <table class='table table-bordered table-striped'> <thead> <tr> <th>#</th> <th>班级ID</th> <th>班级姓名</th> <th>操做</th> </tr> </thead> <tbody> {% for class in class_list %} <tr> <td>{{forloop.counter}}</td> <td scope='row'>{{class.id}}</td> <td class='text-center'> <a href='/edit_class/?class_id = {{class.id}}' type='button' class='btn btn-sm btn-info' aria-label = 'Left Align'> <apan class='glyphicon glyphicon-pencil' aria-hidden='true'></span>新页面编辑 </a> <a type='button' class='btn btn-sm btn-success' aria-label='Left Align'> <span class='glyphicon glyphicon-pencil' aria-hidden='true'></span>编辑 </a> <a href='delete_class/?class_id={{class.id}}' type='button' class='btn btn-sm btn-danger' aria-label='Left Align'> <span class='glyphicon glyphicon glyhicon-remove' aria-hidden='true'></span>删除 </a> </td> </tr> {% endfor%} </tbody> </table>
点击班级列表,表格中对应班级后面的 删除按钮(a标签),向后端发送get请求,而且携带当前班级ID
后端拿到要删除的班级的id值,去数据库中执行删除操做。
def delete_class(request): #根据班级的ID删除 #班级ID从哪来, --->须要页面发起请求的时候携带 #页面如何携带要删除的班级的ID,--->经过给a标签设置href属性时拼接 #后端拿到要删除的班级的ID class_id = request.GET.get('class_id') #去数据里删除 #链接数据库 coon = pymysql.connect(host='localhost', user='root', password='123456', datablase='数据库名', charset=‘utf8’) #指定输出的每条数据的类型是字典 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) #删除操做的SQL语句 sql = 'delete from class where id=%s;' #执行删除语句 cursor.execture(sql, class_id) #向数据库提交操做 conn.commit() cursor.close() conn.close() #执行完删除操做后跳转回班级列表页面 return redirect('/class_list/')
浏览器发起的GET请求经过在URL后面添加/url/?k1 = v1 & k2 = v2的方式添加参数。
使用Django模板语言的语法将{{class.id}}拼接到了a标签的href属性中
这样页面点击这个a标签就能把删除的班级ID值传递给后端了。
<a href='/delete_class/?class_id={{class.id}}' type='button' class='btn btn-sm btn-danger' aria-label='Left Align'> <span class='glyphicon glyphicon-remove' aria-hidden='true'></span>删除 </a>
def add_class(request): #若是是POST请求,就表名是页面要提交数据 if request.method == 'POST': #页面使用的POST方法提交添加班级的数据 #后端取到新班级的名称信息 class_name = request.POST.get('cname') conn = pymysql.connect(host='localhost', user = 'root', password='123456', database='数据库名', charset='utf8') #指定输出的结果类型是字典 cursor = conn.cursor(cursor=pymysql.cursor.DictCursor) #新增班级的SQL语句 sql = 'insert into class (cname) value (%s);' #执行SQL语句,在数据库中建立一条新的班级记录 cursor.execute(sql,class_name) #向数据库提交 conn.commit() #关闭链接 conn.close() #新添加一个班级以后跳转到班级列表页面 return redirect('/class_list/') #若是不是POST请求就返回添加页面 return render(request, 'add_class.html')
利用form表单向后端提交新班级的数据,因此add_class.html页面中form表单要填写上action=‘/add_class/’和method=‘post’
<form action='/add_class/' method='post'> <input type='text' name='cname'> <input type='submit' value='提交'> </form>
def edit_class(request): #若是是POST请求 if request.method == 'POST': #从页面POST过来的数据中取到班级名称 class_cname = request.POST.get('id') #去数据库里面更新班级名称 #链接数据库 conn = pymysql.connect(host='localhost', user='root', password='123456', database='数据库名', charset='utf8') #获取光标 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) #更新编辑信息的SQL语句 sql = 'update class set cname=%s where id=%s;' #执行SQl语句 cursor.execute(sql, (class_cname,class_id)) #向数据库提交修改 conn.commit() #关闭链接 cursor.close() conn.close() #更新完班级信息以后跳转回班级列表页面 return redirect('/class_list/') #若是不是POST请求,就表名是点击了新页面编辑班级信息的按钮 #取到被编辑的班级的ID class_id = request.GET.get('class_id') #去数据库里查询当前班级的信息 conn = pymysql.connect(host='localhost', user='root', password='123456', database='数据库名', charset='utf8') #指定输出的单挑数据的类型是字典 cursor = conn.cursor(curso=pymysql.cursors.DictCursor) #查询的SQL语句 sql = ‘select id, cname from class where id = %s;’ #执行查询的操做 cursor.execute(sql, class_id) #取单条数据 ret = cursor.fetchone() #关闭链接 cursor.close() conn.close() #把要编辑的班级的信息 替换edit_class.html 页面中的特殊符号,最后把页面返回给浏览器 return render(request, 'edit_class.html', {'class_info':ret})
编辑班级信息在页面上展现的就是一个form表单,用户更新完班级信息以后要提交,就须要向后端提交表单数据。
这个时候form表单就须要配置好action=‘/edit_class/’和method='post'.
可是提交的数据里须要告知后端,我更改的数据库中的那一条班级信息,因此这里要把班级的ID也放到form表单中。
班级ID不该该让用户修改,因此就把班级ID的input框给隐藏掉。
<form action='/edit_class/' method='post'> <input type='text' name='id' vlaue='{{class_info.id}}' style='display:none'> <input type='text' name='cname' value='{{class_info.cname}}'> <input type='subimt' value='提交'> </form>
首先,咱们来回顾下到目前为止咱们学过的Django框架相关的内容:
浏览器输入URL到页面展现结果的过程,能够参考下图:
上图中,绿色部分就是咱们实际须要开发的那部分。
学生信息的管理相比于班级信息的管理稍微难一点,由于数据库的表结构中的学生表经过外键关联了班级表。
因此增删改查的操做须要注意外键部分的相关操做。
学生信息的展现(查)
区别于班级信息的管理,学生信息由于经过外键关联了班级信息,因此除了要展现学生的姓名还要展现出学生所属班级的名称。
def student_list(request): conn = pymysql.connect(host='127.0.0.1',port=3306,user='root', password='123456', db='mysite', charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.excute('select student.id, sutdent.name, class.name AS class_name from student LEFT JOIN class ON student.class_id = class.id;') student_list = cursor.fetchall() cursor.close() conn.close() return render(request, 'student_list.html', {'students’:student_list})
<table class='table talble-bordered table_striped'> <thead> <tr> <th>#</th> <th>学生姓名</th> <th>班级</th> <th>操做</th> </tr> </thead> <tbody> {% for student in students %} <tr> <th scopt='row'>{{ student.id }}</th> <td>{{student.name}}</td> <td>{{student.class_name}}</td> <td class='text-conter'> <a type='button' class='btn btn-sm btn-success' aria-label='Left Align'> <span class='glyphicon glyphicon-pencil' aria-hidden='true'></span>编辑 </a> <a href='/delete_student/?student_id{{student.id}}' type='button' class='btn btn-sm btn-danger' aria-label='Left Align'> <span class='glyphicon glyphicon-pencil' aria-hidden='true'> </span>新页面编辑 </a> <a href='/delete_student/?student_id={{student.id}}/' type='button' class='btn btn-sm btn-success' aria-label='Left Align'> <span class='glyphicon glyphicon-remove' aria-hidden='true'> </span>删除 </a> </td> <tr> {% endfor %} </tbody> </table>
再一次练习了Django模板语言的for循环
def delete_student(request): #从GET请求的URL中取到要删除的学生ID student_id = request.get('student_id') #链接数据库 conn = pymysql.connect(host='localhost', port=3306, user='root', password='123456', database='数据库名',charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) #删除指定的学生 sql = 'delete from student Where id='%s;' #执行SQL语句 cursor.execute(sql, [student_id,]) conn.commit() conn.close() #删除成功,跳转到学生列表页 return redirect('/student_list/')
注意在学生信息页面删除按钮上用的模板语言的方式拼接student_id。
<a href='/delete_student/?student_id = {{student.id}}' type='button' class='btn btn-sm btn-danger' aria-label='Left Align'> <span class='glyphicon glyphicon-remove' aria-hidden='true'></span>编辑 </a>
def add_student(request): #若是是POST请求表示前端提交数据过来 if request.method == 'POST': student_name = request.POST.get('student_name') class_id = remysql.connect(host='127.0.0.1', port='3306', user='root', password='123456', db='数据库名', charset='utf8') cursor = conn.cursor(cursor = pymysql.cursors.DictCursor) cursor = execute('inset into student(name, class_id) value(%s, %s)', [student_name, class_id]) conn.commit() cursor.close() conn.close() return redirect('/student_list/') #前端不发送POST请求状况下默认返回新增学生信息页面 else: #由于咱们新添加学生信息的时候须要指定所属的班级 #因此须要先查询出全部的班级信息,填充到页面上 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='123456', database='数据库名', charset='utf8') cursor=conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute('select id, name from class') class_list = cursor.fetchall() cursor.close() conn.close() return render(request, 'add_student.html', {'class_list':class_list})
前端页面须要将已经有的班级信息作成能够选择的select框。
<form class='form-horizontal' action='/add_student/' method='post'> <div class='form-group'> <label for='inputclassname' class='col-sm-2 control-label'>学生名</label> <div class='col-sm-10'> <input type='text' class='form-control' name='student_name' id='inputclassname' placeholder='学生姓名'> </div> </div> <div class='form-group'> <label for='selectclass' class='col-sm-2' control-albel>班级</label> <div class='col-sm-10'> <select class='form-control' name='class-id'> {% for class in class_list %} <option value='{{class.id}}'>{{class.name}}</option> {% endfor %} </select> </div> </div> </div class='form-group'> <div class='col-sm-offset-2 col-sm-10'> <button type='submit' class='btn btn-default'>提交</button> </div> </div> </form>
def edit_student(request): if request.method == 'POST': student_id = request.POST.get('student_id') student_name = request.POST.get('student_name') class_id = request.POST.get('class_id') #更新学生表的SQL sql = 'updata student set name=%s, class_id = %s where id = %s;' #链接数据库 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='123456', database='数据库名', charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute(sql, [student_name, class_id, student_id]) cursor.close() conn.close() #更新完学生信息以后跳转到学生列表页面 return redirect('/student_list/') else: #要编辑学生信息就须要在页面上把当前学生信息以及全部的班级信息都展现出来 #取到要编辑的学生的ID student_id = request.GET.get('sutdent_id') #链接数据库 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='123456', database='数据库名',charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) #取到全部的班级信息 get_class_sal = 'select id, name from class;' cursor.execute(get_class_sql) class_list = cursor.fetchall() get_student_sql = 'select id, name, class_id from student where id=%s;' cursor.execute(get_student_sql, [student_id]) student = cursor.fetchone() cursor.close() conn.close() return render(request, 'edit_student.html', {'class_list':class_list, 'student': student})
<form class = 'form-horizontal' action='/edit_student/' method='post'> <input type='text' name='student_id' value='{{student.id}}' style='display: none'> <div class='form-group'> <label for='inputclassname' class='col-sm-2 control-label'>学生姓名</label> <div class='col-sm-10'> <input type='text' class='form-control' name='student_name' id='inputclassname' placeholder='班级名称' value='{{student.name}}'> </div> <span id='helpBlock2' class='help-block'>{{error}}</span> </div> <div class='col-sm-10'> <select class='form-control' name='class_id'> {% for class in class_list %} {% if class.id == student.class_id %} <option selected value='{{class.id}}'>{{class.name}}</option> {% else %} <option value='{{class.id}}'> {{class.name}}</option> <% endif %> <% endfor %> </select> </div> </div> <div class = 'form-group'> <div class='col-sm-offset-2 col-sm-10'> <button type='submit' class='btn btn-default'>提交</button> </div> </div> </form>
老师信息管理相比于学生信息管理又多了一点,由于咱们的数据结构中老师表和班级表是经过teacher2class表进行多对多关联的。
注意连表查询操做
def teacher_list(request): #连表数据库 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='123456', db='数据库名',charset='utf8') cursor = conn.cursor(cursor = pymysql.cursors.DictCursor) sql = 'select teacher.id, teacher.name, class.name as cname from teacher LEFT JOIN teacher2class on teacher.id = teacher2class.teacher_id LEFT JOIN class on teacher2class.class_id = class.id;' curosr.execute(sql) teacher_list_o = cursor.fetchall() #将查询到的数据类型转换一下 teacher_list = magic(teacher_list_o) return render(request, 'teacher_list.html', {'teahcer_list':teacher_list})
未来时关联的班级信息展现出来
<table class='talbe table-bordered table-striped'> <thead> <tr> <th>#</th> <th>老师</th> <th>班级</th> <th>操做</th> </tr> </thead> <tbody> {% for teacher in teacher_list %} <tr> <th scipe='row'>{{teacher.id}}</th> <td>{{teacher.name}}</td> <td> {% for class in teacher.class_list %} <span>{{ class }}</span> {% endfor %} </td> <th class='text-center'> <a type='button' class='btn btn-sm btn-success m-edit' aria-label='Left Align'> <span class='glyphicon glyphicon-pencil' aria-hidden='true'></span>编辑</a> <a href='/edit_teacher/?teacher_id={{teacher.id}}/' type = 'button' class='btn btn-sm btn-success' aria-label='Left Align'> <span class='glyphicon glyphicon-pencil' aria-hidden='true'></span>新页面编辑</a> <a href='/delete_teacher/?teacher_id={{teacher.id}}' type='button' class='btn btn-sm btn-danger' aria-label='Left Align'> <span calss='glyphicon glyphicon-remove' aria-hidden='true'></span>删除</a> </td> </tr> {% empty %} <tr><td colspan='3' style='text-align:center'>空空如也</td></tr> <% endfor %> </tbody> <table>
后端从请求的URL中提取到要删除的老师的ID
def delete_teacher(request): #从GET请求的URL中取到要删除的老师ID teacher_id = request.GET.get('student_id') #链接数据库 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='123456', database='数据库名',charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) #删除指定的老师 sql = 'delete from teacher WHERE id='%s;' #执行sql语句 cursor.execute(sql, [teacher_id,]) conn.commit() conn.close() #删除成功,跳转到老师列表页 return redirect('/teacher_list/')
点击页面上对应老师记录的删除按钮, 向后端发送GET请求,将要删除的老师的ID传递给后端。
删除按钮部分HTML代码:
<a href='/delete_teacher/?teacher_id={{chass.id}}' type='button' class='btn btn-sm btn-danger' aria-label='Left Align'> <span class='glyphicon glyphicon-remove' aria-hidden='true'></span>删除</a>
这里咱们对咱们的数据库链接部分的代码作了优化
from tools.sql_master import create, SQLManager def add_teacher(request): if request.method == 'POST': class_list = request.POST.getlist('class_id') teacher_name = request.POST.get('teacher_name') #建立老师 teacher_id = create('insert into teahcer(name) values (%s)', [teacher_name, ]) #更新teacher2class表 #屡次链接,屡次提交 #for i in class_list: # modify('insert into teacher2class(teacher_id, class_id) values (%s, %s)', [teacher_id,i]) 一次链接,屡次提交 #db = SQLManager() #for i in class_list: # db.moddify('insert into teacher2class(teacher_id, class_id) values (%s, %s)', [teacher_id, i]) #db.close() #一次链接,一次提交 data_list = [] for i in class_list: tmp = [teacher_id, i] data_list.append(tmp) db = SQLManager() db.multi_modify('insert into teacher2class(teacher_id, class_id) values (%s, %s)', data_list) db.close() return redirect('/teacher_list/') else: class_list = get_list('select id, name from class') return render(request, 'add_teacher.html', {'class_list': class_list})
由于咱们添加老师信息的时候,须要给老师指定关联的班级信息,所在页面上须要将现有的班级信息展现出来。
新增老师信息,输入老师的姓名而后选择关联的班级信息便可。
<form calss='form-horizontal' action='/add_teacher/' method='post'> <div class='form-group'> <label for='inputclassname' class='col-sm-2 control-label'>老师姓名</label> <div class='col-sm-10'> <input type='text' class='form-control' name='teacher_name' id='inputclassname' placeholder='老师姓名'> </div> </div> <div class='form-group'> <label for='selectclass' class='col-sm-2 control-label'>班级 </label> <div class='col-sm-10'> <select class='form-control' name='class_id' multiple> {% for class in class_list %} <option value='{{ class.id }}'>{{class.name}}</option> {% endfor %} </select> </div> </div> <div class='form-group'> <div class='col-sm-offset-2 con-sm-10'> <button type='submit' class='btn btn-default'>提交</button> <div> </div> </form>
def edit_teacher(request): if request.method == 'POST': teacher_id = request.POST.get('teacher_id') class_ids = request.POST.getlist('class_id') #更新 db = SQLManager() teacher_class_ids = db.get_list('select class_id from teacher2class Where teacher_id=%s',[teacher_id, ]) old_class_ids = [i['class_id'] for i in teacher_class_ids] #粗暴更新 del_id_list = [] add_id_list = [] for i in old_class_ids: del_id_list.append((teacher_id, i)) for j in class_ids: add_id_list.append((teacher_id,j)) db.multi_modify('insert into techaer2class(teacher_id, class_id) values (%s, %s)', add_id_list) db.close() return redirect('/teacher_list/') else: teacher_id = request.GET.get('teacher_id') with SQLManager() as db: class_list = db.get_list('select id, name from class') teacher_info = db.get_list('select teacher.id, teacher.name, teacher2class.class_id FROM teacher LEFR JOIN teacher2class ON teacher.id = teacher2class.teacher_id weher teacher.id=%s;', [teacher_id]) ret = teacher_info[0] ret['class_ids'] = [ret['class_id'], ] for i in teacher_info[1:]: ret['class_ids'].append(i['class_id']) return render(request, 'endit_teacher.html', {'class_list':class_list, 'teacher': ret})
<form class='form-horizontal' action='/edit_teacher/' method='post'> <input type='text' name='teacher_id' value='{{teacher.id}}' style = 'display:none'> <div class='form-group'> <label for='inputclassname' class='col-sm-2 control-label'>学生姓名</label> <div class='col-sm-10'> <input type='text' class='form-control' name='teacher_name' id='inputteachername' placeholder='老师姓名' value='{{teacher.name}}'> </div> <span id='helpBlock2' class='help-block'>{{error}}</span> <div> <div class='form-group'> <label for='selectclass' class='col-sm-2 control-label'>班级</label> <div class='col-sm-10'> <select class='form-control' name='class_id' multiple> <% for class in calss_list %> {% if class.id in teacher.class_ids %} <option selected value='{{ class.id }}'>{{ class.name }}</option> <% else %> <option value='{{class.id}}'>{{class.name}}</option> <% endif %> <% enfor %> </select> </div> </div> <div class='form-group'> <div class='col-sm-offset-2 col-sm-10'> <button type='submit' class='btn btn-default'>提交</button> </div> </div> </form>
以前使用pymysql操做数据库的操做都是卸载视图函数中,而且不少都是重复代码。
咱们能够优化一下,把重复的代码提取出来,写成函数:
import pymysql #定义一个数据库相关的配置项 DB_CONFIG{ ‘host’:‘127.0.0.1’, ‘port’ : 3306, ‘user’:‘root’, ‘password’:‘123456’ ‘db’:‘数据库名’, ‘charset’:‘utf8’}
这样只要在须要链接数据库作操做的时候,只须要调用咱们上面定义好的函数就能够了。
但这样仍是有问题,当我要大批量建立数据库的时候,就须要屡次调用create方法了,至关于屡次链接屡次提交。
能够继续优化下,把数据库的链接重用,作到只需一次链接就能够屡次操做。
class SQlManger(object): #初始化实例方法 def __int__(self): self.conn = None self.cursor = None self.connect() #链接数据库 def connect(self): self.conn = pymysql.connect( host = DB_CONFIG['host'], port = DB_CONFIG['port'], user = DB_CONFIG['user'] password = DB_CONFIG['passwrod'] db = DB_CONFIG['db'] charset = DB_CONFIG['charset'] ) self.cursor = self.conn.cursor(cursro=pymysql.cursors.DictCursor) #查询多天数据 def get_lilst(self, sql, args=None): self.cursor.execte(sql, args) result = self.cursor.fetchall() return result #查询单条数据 def get_one(self, sql, args = None): self.cursor.execute(sql, args) result = self.cursor.fetchone() return result #执行单条SQL语句 def moddify(self, sql, args = None): self.cursor.execute(sql, args) self.conn.commit() #建立单条记录的语句 def create(self, sql, args=None): self.cursor.execute(sql, args) self.conn.commit() last_id = self.cursor.lastrowid return last-id #关闭数据库cursor和链接 def close(self): self.cursor.close() self.conn.close()
咱们把咱们数据库的相关操做都封装成一个类,在用到的时候,