In our previous Python project tutorial, we have explained to Face Recognition Based Attendance System with Python. In this tutorial, we will explain how to develop School Management System with Python, Flask and MySQL.
School Management Systems (SMS) is an online system that’s used in schools to manage students, classes, sections, subjects, teachers and attendance.
Here in this tutorial, we haved developed a very simple school management systems for learning purpose and can be enhanced according to requirement to develop a perfect advance level system.
We will cover following in this tutorial:
- Manage User Login
- Manage Teachers
- Manage Students
- Manage Classes
- Manage Sections
- Manage Subjects
- Manage Attendance
- Attendance Report
1. MySQL Database
First we will create MySQL database python_sms
and create tables for our system.
We will create table sms_teacher
to store teachers details.
CREATE TABLE `sms_teacher` ( `teacher_id` int(11) NOT NULL, `teacher` varchar(255) NOT NULL, `subject_id` int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1; ALTER TABLE `sms_teacher` ADD PRIMARY KEY (`teacher_id`);
We will create table sms_students
to store students details.
CREATE TABLE `sms_students` ( `id` int(10) UNSIGNED NOT NULL, `name` varchar(40) NOT NULL, `gender` varchar(40) NOT NULL, `dob` varchar(255) NOT NULL, `photo` varchar(40) DEFAULT NULL, `mobile` int(10) UNSIGNED NOT NULL, `email` varchar(40) DEFAULT NULL, `current_address` varchar(40) DEFAULT NULL, `permanent_address` varchar(40) DEFAULT NULL, `father_name` varchar(255) NOT NULL, `father_mobile` int(10) UNSIGNED NOT NULL, `father_occupation` varchar(255) NOT NULL, `mother_name` varchar(255) NOT NULL, `mother_mobile` int(10) UNSIGNED NOT NULL, `admission_no` int(11) NOT NULL, `roll_no` int(11) NOT NULL, `class` int(10) UNSIGNED NOT NULL, `section` int(11) NOT NULL, `stream` int(10) UNSIGNED DEFAULT NULL, `hostel` int(10) UNSIGNED DEFAULT NULL, `admission_date` varchar(255) NOT NULL, `category` int(10) UNSIGNED DEFAULT NULL, `academic_year` int(10) UNSIGNED NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `sms_students` ADD PRIMARY KEY (`id`);
We will create table sms_classes
to store school class details.
CREATE TABLE `sms_classes` ( `id` int(10) UNSIGNED NOT NULL, `name` varchar(40) NOT NULL, `section` varchar(255) NOT NULL, `teacher_id` int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `sms_classes` MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;
We will create table sms_section
to store school class section details.
CREATE TABLE `sms_section` ( `section_id` int(11) NOT NULL, `section` varchar(255) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1; ALTER TABLE `sms_section` MODIFY `section_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;
We will create table sms_subjects
to store subject details.
CREATE TABLE `sms_subjects` ( `subject_id` int(11) NOT NULL, `subject` varchar(255) NOT NULL, `type` varchar(255) NOT NULL, `code` int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1; ALTER TABLE `sms_subjects` MODIFY `subject_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
We will create table sms_attendance
to store students attendance details.
CREATE TABLE `sms_attendance` ( `attendance_id` int(11) NOT NULL, `student_id` int(11) NOT NULL, `class_id` int(11) NOT NULL, `section_id` int(11) NOT NULL, `attendance_status` int(11) NOT NULL, `attendance_date` varchar(255) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1; ALTER TABLE `sms_attendance` MODIFY `attendance_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9;
We will create table sms_user
to store user login details to access system.
CREATE TABLE `sms_user` ( `id` int(11) NOT NULL, `first_name` varchar(50) NOT NULL, `last_name` varchar(50) NOT NULL, `email` varchar(50) NOT NULL, `password` varchar(50) NOT NULL, `gender` enum('male','female') CHARACTER SET utf8 NOT NULL, `mobile` varchar(50) NOT NULL, `designation` varchar(50) NOT NULL, `image` varchar(250) NOT NULL, `type` varchar(250) NOT NULL DEFAULT 'general', `status` enum('active','pending','deleted','') NOT NULL DEFAULT 'pending', `authtoken` varchar(250) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1; ALTER TABLE `sms_user` ADD PRIMARY KEY (`id`);
2. Setup Porject File
We will create our application file app.py
and import required modules. We will setup our Flask app and also configure to connect with MySQL database. Here we will connect to python_sms
MySQL database.
from flask import Flask, render_template, request, redirect, url_for, session, jsonify from flask_mysqldb import MySQL import MySQLdb.cursors from datetime import date import re import os import sys app = Flask(__name__) app.secret_key = 'abcd21234455' app.config['MYSQL_HOST'] = 'localhost' app.config['MYSQL_USER'] = 'root' app.config['MYSQL_PASSWORD'] = '' app.config['MYSQL_DB'] = 'python_sms' mysql = MySQL(app)
3. User Login
We will create route in app.js
for login and create login()
function to implement user login functionality to access system. The login.html
template file will be called for displaying user login form. The login functionality willl be handled when login form submitted.
@app.route('/') @app.route('/login', methods =['GET', 'POST']) def login(): mesage = '' if request.method == 'POST' and 'email' in request.form and 'password' in request.form: email = request.form['email'] password = request.form['password'] cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT * FROM sms_user WHERE status="active" AND email = % s AND password = % s', (email, password, )) user = cursor.fetchone() if user: session['loggedin'] = True session['userid'] = user['id'] session['name'] = user['first_name'] session['email'] = user['email'] session['role'] = user['type'] mesage = 'Logged in successfully !' return redirect(url_for('dashboard')) else: mesage = 'Please enter correct email / password !' return render_template('login.html', mesage = mesage)
We will create login.html
template file and create login form html.
{% include 'header.html' %} <body> <div class="container-fluid" id="main"> {% include 'top_menus.html' %} <div class="row row-offcanvas row-offcanvas-left"> <div class="col-md-9 col-lg-10 main"> <h2>User Login</h2> <form action="{{ url_for('login') }}" method="post"> {% if mesage is defined and mesage %} <div class="alert alert-warning">{{ mesage }}</div> {% endif %} <div class="form-group"> <label for="email">Email:</label> <input type="email" class="form-control" id="email" name="email" placeholder="Enter email" name="email"> </div> <div class="form-group"> <label for="pwd">Password:</label> <input type="password" class="form-control" id="password" name="password" placeholder="Enter password" name="pswd"> </div> <button type="submit" class="btn btn-primary">Login</button> </form> <hr> </div> </div> </div> </body> </html>
4. Manage Teachers
We will create route in app.js
for teacher and get details. We will load teacher.html
and pass teachers details to display.
@app.route("/teacher", methods =['GET', 'POST']) def teacher(): if 'loggedin' in session: cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT t.teacher_id, t.teacher, s.subject FROM sms_teacher t LEFT JOIN sms_subjects s ON s.subject_id = t.subject_id') teachers = cursor.fetchall() cursor.execute('SELECT * FROM sms_subjects') subjects = cursor.fetchall() return render_template("teacher.html", teachers = teachers, subjects = subjects) return redirect(url_for('login'))
We will create teacher.html
template file and create HTML to list teachers.
{% include 'top_menus.html' %} <div class="container"> {% include 'left_menus.html' %} <div class="content"> <div class="container-fluid"> <div class="row"> <a href="#"><strong><span class="ti-crown"></span> Teachers Section</strong></a> <hr> <div class="float-right mb-2 col-md-2"> <button type="button" id="addTeacher" class="btn btn-info" title="Add teacher"><span class="glyphicon glyphicon-plus">Add</span></button> </div> <br><br> <table class="table table-striped"> <thead> <tr> <th>id</th> <th>Name</th> <th>Specialization</th> <th></th> <th></th> </tr> </thead> <tbody> {% for teacher in teachers %} <tr> <td>{{teacher.teacher_id}}</td> <td>{{teacher.teacher}}</td> <td>{{teacher.subject}}</td> <td><a href="{{url_for('edit_teacher', teacher_id=teacher.teacher_id)}}" id="{{teacher.teacher_id}}" class="btn btn-primary update">Edit</a></td> <td><a href="{{url_for('delete_teacher', teacher_id=teacher.teacher_id)}}" class="btn btn-danger">Delete</a></td> </tr> {% endfor %} </tbody> </table> <hr> </div> </div> </div> </div>
5. Manage Students
We will create route in app.js
for student and get students details. We will load student.html
and student details.
@app.route("/student", methods =['GET', 'POST']) def student(): if 'loggedin' in session: cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT s.id, s.admission_no, s.roll_no, s.name, s.photo, c.name AS class, sec.section FROM sms_students s LEFT JOIN sms_section sec ON sec.section_id = s.section LEFT JOIN sms_classes c ON c.id = s.class') students = cursor.fetchall() cursor.execute('SELECT * FROM sms_classes') classes = cursor.fetchall() cursor.execute('SELECT * FROM sms_section') sections = cursor.fetchall() return render_template("student.html", students = students, classes = classes, sections = sections) return redirect(url_for('login'))
We will create student.html
template file and create HTML to list student.
{% include 'top_menus.html' %} <div class="container"> {% include 'left_menus.html' %} <div class="content"> <div class="container-fluid"> <div class="row"> <a href="#"><strong><span class="ti-crown"></span> Student Section</strong></a> <hr> <div class="panel-heading"> <div class="row"> <div class="col-md-10"> <h3 class="panel-title"></h3> </div> <div class="col-md-2" align="right"> <button type="button" name="add" id="addStudent" class="btn btn-success btn-xs">Student Admission</button> </div> </div> </div> <table id="studentList" class="table table-bordered table-striped"> <thead> <tr> <th>ID</th> <th>Reg No</th> <th>Roll No</th> <th>Name</th> <th>Photo</th> <th>Class</th> <th>Section</th> <th></th> <th></th> </tr> </thead> <tbody> {% for student in students %} <tr> <td>{{student.id}}</td> <td>{{student.admission_no}}</td> <td>{{student.roll_no}}</td> <td>{{student.name}}</td> <td>{{student.photo}}</td> <td>{{student.class}}</td> <td>{{student.section}}</td> <td>{{student.section}}</td> <td><a href="{{url_for('edit_student', student_id=student.id)}}" class="btn btn-primary update">Edit</a></td> <td><a href="{{url_for('delete_student', student_id=student.id)}}" class="btn btn-danger">Delete</a></td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </div>
6. Manage Classes
We will create route in app.js
for class and get class details. We will load class.html
template file and pass classes details to list.
@app.route("/classes", methods =['GET', 'POST']) def classes(): if 'loggedin' in session: cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT c.id, c.name, s.section, t.teacher FROM sms_classes c LEFT JOIN sms_section s ON s.section_id = c.section LEFT JOIN sms_teacher t ON t.teacher_id = c.teacher_id') classes = cursor.fetchall() cursor.execute('SELECT * FROM sms_section') sections = cursor.fetchall() cursor.execute('SELECT * FROM sms_teacher') teachers = cursor.fetchall() return render_template("class.html", classes = classes, sections = sections, teachers = teachers) return redirect(url_for('login'))
We will create class.html
template file and create HTML to list classes.
{% include 'top_menus.html' %} <div class="container"> {% include 'left_menus.html' %} <div class="content"> <div class="container-fluid"> <div class="row"> <a href="#"><strong><span class="ti-crown"></span> Classes Section</strong></a> <hr> <div class="panel-heading"> <div class="row"> <div class="col-md-10"> <h3 class="panel-title"></h3> </div> <div class="col-md-2" align="right"> <button type="button" name="add" id="addClass" class="btn btn-success btn-xs">Add New Class</button> </div> </div> </div> <table id="classList" class="table table-bordered table-striped"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Sections</th> <th>Class Teacher</th> <th></th> <th></th> </tr> </thead> <tbody> {% for class in classes %} <tr> <td>{{class.id}}</td> <td>{{class.name}}</td> <td>{{class.section}}</td> <td>{{class.teacher}}</td> <td><a href="{{url_for('edit_class', class_id=class.id)}}" class="btn btn-primary update">Edit</a></td> <td><a href="{{url_for('delete_class', class_id=class.id)}}" class="btn btn-danger">Delete</a></td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </div>
7. Manage Sections
We will create route in app.js
for sections. We will get sections details and pass with template file to list.
@app.route("/sections", methods =['GET', 'POST']) def sections(): if 'loggedin' in session: cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT * FROM sms_section') sections = cursor.fetchall() return render_template("sections.html", sections = sections) return redirect(url_for('login'))
We will create section.html
template file and create HTML to list sections.
{% include 'top_menus.html' %} <div class="container"> {% include 'left_menus.html' %} <div class="content"> <div class="container-fluid"> <div class="row"> <a href="#"><strong><span class="ti-crown"></span> Classes Section</strong></a> <hr> <div class="panel-heading"> <div class="row"> <div class="col-md-10"> <h3 class="panel-title"></h3> </div> <div class="col-md-2" align="right"> <button type="button" name="add" id="addSection" class="btn btn-success btn-xs">Add New Section</button> </div> </div> </div> <table id="sectionList" class="table table-bordered table-striped"> <thead> <tr> <th>ID</th> <th>Name</th> <th></th> <th></th> </tr> </thead> <tbody> {% for section in sections %} <tr> <td>{{section.section_id}}</td> <td>{{section.section}}</td> <td><a href="{{url_for('edit_sections', section_id=section.section_id)}}" id="{{section.section_id}}" class="btn btn-primary update">Edit</a></td> <td><a href="{{url_for('delete_sections', section_id=section.section_id)}}" class="btn btn-danger">Delete</a></td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </div>
8. Manage Subjects
We will create route in app.js
for subject and get subjetc details. We will load template file and passed details to list.
@app.route("/subject", methods =['GET', 'POST']) def subject(): if 'loggedin' in session: cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT * FROM sms_subjects') subjects = cursor.fetchall() return render_template("subject.html", subjects = subjects) return redirect(url_for('login'))
We will create subject.html
template file and create HTML to list subjects.
{% include 'top_menus.html' %} <div class="container"> {% include 'left_menus.html' %} <div class="content"> <div class="container-fluid"> <div class="row"> <a href="#"><strong><span class="ti-crown"></span> Subjects Section</strong></a> <hr> <div class="panel-heading"> <div class="row"> <div class="col-md-10"> <h3 class="panel-title"></h3> </div> <div class="col-md-2" align="right"> <button type="button" name="add" id="addSubject" class="btn btn-success btn-xs">Add</button> </div> </div> </div> <table id="subjectList" class="table table-bordered table-striped"> <thead> <tr> <th>ID</th> <th>Subject</th> <th>Code</th> <th>Subject Type</th> <th></th> <th></th> </tr> </thead> <tbody> {% for subject in subjects %} <tr> <td>{{subject.subject_id}}</td> <td>{{subject.subject}}</td> <td>{{subject.code}}</td> <td>{{subject.type}}</td> <td><a href="{{url_for('edit_subject', subject_id=subject.subject_id)}}" class="btn btn-primary update">Edit</a></td> <td><a href="{{url_for('delete_subject', subject_id=subject.subject_id)}}" class="btn btn-danger">Delete</a></td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </div>
9. Manage Attendance
We will create route in app.js
for attendance and get attendance details.
@app.route("/getClassAttendance", methods =['GET', 'POST']) def getClassAttendance(): if 'loggedin' in session: if request.method == 'POST' and 'classid' in request.form and 'sectionid' in request.form: classid = request.form['classid'] sectionid = request.form['sectionid'] cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT * FROM sms_classes') classes = cursor.fetchall() cursor.execute('SELECT * FROM sms_section') sections = cursor.fetchall() currentDate = date.today().strftime('%Y/%m/%d') cursor.execute('SELECT s.id, s.name, s.photo, s.gender, s.dob, s.mobile, s.email, s.current_address, s.father_name, s.mother_name,s.admission_no, s.roll_no, s.admission_date, s.academic_year, a.attendance_status, a.attendance_date FROM sms_students as s LEFT JOIN sms_attendance as a ON s.id = a.student_id WHERE s.class = '+classid+' AND s.section = '+sectionid) students = cursor.fetchall() return render_template("attendance.html", classes = classes, sections = sections, students = students, classId = classid, sectionId = sectionid) elif request.method == 'POST': msg = 'Please fill out the form field !' return redirect(url_for('attendance')) return redirect(url_for('login'))
We will create attendance.html
template file and create HTML to list attendance.
{% include 'top_menus.html' %} <div class="container"> {% include 'left_menus.html' %} <div class="content"> <div class="container-fluid"> <div class="row"> <div class="col-md-12"> <div class="box box-primary"> <div class="box-header with-border"> <h3 class="box-title"><i class="fa fa-search"></i> Select Criteria</h3> </div> <form id="form1" action="{{ url_for('getClassAttendance')}}" method="post" accept-charset="utf-8"> <div class="box-body"> <div class="row"> <div class="col-md-4"> <div class="form-group"> <label for="exampleInputEmail1">Class</label><small class="req"> *</small> <select id="classid" name="classid" class="form-control" required> <option value="">Select</option> {% for class in classes %} <option value="{{class.id}}" {% if class.id == classId %} selected {% endif %}>{{class.name}}</option> {% endfor %} </select> <span class="text-danger"></span> </div> </div> <div class="col-md-4"> <div class="form-group"> <label for="exampleInputEmail1">Section</label><small class="req"> *</small> <select name="sectionid" id="sectionid" class="form-control" required> <option value="">Select</option> {% for section in sections %} <option value="{{section.section_id}}" {% if section.section_id == sectionId %} selected {% endif %}>{{section.section}}</option> {% endfor %} </select> <span class="text-danger"></span> </div> </div> </div> </div> <div class="box-footer"> <button type="submit" id="search" name="search" value="search" style="margin-bottom:10px;" class="btn btn-primary btn-sm checkbox-toggle"><i class="fa fa-search"></i> Search</button> <br> </div> </form> </div> <div class="row"> <form id="attendanceForm" method="post"> <div style="color:red;margin-top:20px;" class="" id="message"></div> <button type="submit" id="saveAttendance" name="saveAttendance" value="Save Attendance" style="margin-bottom:10px;" class="btn btn-primary btn-sm pull-right checkbox-toggle hidden"><i class="fa fa-save"></i> Save Attendance</button> <table id="studentList" class="table table-bordered table-striped"> <thead> <tr> <th>#</th> <th>Reg No</th> <th>Roll No</th> <th>Name</th> <th>Attendance</th> </tr> </thead> <tbody> {% for student in students %} <tr> <td>{{student.id}}</td> <td>{{student.admission_no}}</td> <td>{{student.roll_no}}</td> <td>{{student.name}}</td> <td> <input type="radio" id="attendencetype1_"+{{student.id}}+" value="1" name="attendencetype_"+{{student.id}}+" autocomplete="off" '.$checked['1'].'> <label for="attendencetype_"+{{student.id}}+">Present</label> <input type="radio" id="attendencetype2_"+{{student.id}}+"" value="2" name="attendencetype_"+{{student.id}}+"" autocomplete="off" '.$checked['2'].'> <label for="attendencetype"+{{student.id}}+">Late </label> <input type="radio" id="attendencetype3_"+{{student.id}}+" value="3" name="attendencetype_"+{{student.id}}+" autocomplete="off" '.$checked['3'].'> <label for="attendencetype3_"+{{student.id}}+"> Absent </label> <input type="radio" id="attendencetype4_"+{{student.id}}+" value="4" name="attendencetype_"+{{student.id}}+" autocomplete="off" '.$checked['4'].'><label for="attendencetype_"+{{student.id}}+"> Half Day </label> </td> </tr> {% endfor %} </tbody> </table> <input type="hidden" name="action" id="action" value="updateAttendance" /> <input type="hidden" name="att_classid" id="att_classid" value="" /> <input type="hidden" name="att_sectionid" id="att_sectionid" value="" /> </form> </div> </div> </div> </div> </div> </div>
10. Implement Attendance Report
We will create route in app.js
for attendance report and get details to list.
@app.route("/report", methods =['GET', 'POST']) def report(): if 'loggedin' in session: cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor) cursor.execute('SELECT * FROM sms_classes') classes = cursor.fetchall() cursor.execute('SELECT * FROM sms_section') sections = cursor.fetchall() return render_template("report.html", classes = classes, sections = sections) return redirect(url_for('login'))
We will create report.html
template file and create HTML to list attendance report.
{% include 'top_menus.html' %} <div class="container"> {% include 'left_menus.html' %} <div class="content"> <div class="container-fluid"> <div class="row"> <div class="col-md-12"> <div class="box box-primary"> <div class="box-header with-border"> <h3 class="box-title"><i class="fa fa-search"></i> Select Criteria</h3> </div> <form id="form1" action="" method="post" accept-charset="utf-8"> <div class="box-body"> <div class="row"> <div class="col-md-4"> <div class="form-group"> <label for="classid">Class</label><small class="req"> *</small> <select id="classid" name="classid" class="form-control" required> <option value="">Select</option> {% for class in classes %} <option value="{{class.id}}" {% if class.id == classId %} selected {% endif %}>{{class.name}}</option> {% endfor %} </select> <span class="text-danger"></span> </div> </div> <div class="col-md-4"> <div class="form-group"> <label for="sectionid">Section</label><small class="req"> *</small> <select name="sectionid" id="sectionid" class="form-control" required> <option value="">Select</option> {% for section in sections %} <option value="{{section.section_id}}" {% if section.section_id == sectionId %} selected {% endif %}>{{section.section}}</option> {% endfor %} </select> <span class="text-danger"></span> </div> </div> <div class="col-md-4"> <div class="form-group"> <label for="attendanceDate">Attendance Date</label><small class="req"> *</small> <input type='text' class="form-control" id="attendanceDate" name="attendanceDate" value="2019/06/22" placeholder="yyyy/mm/dd" /> </div> </div> </div> </div> <div class="box-footer"> <button type="button" id="search" name="search" value="search" style="margin-bottom:10px;" class="btn btn-primary btn-sm checkbox-toggle"><i class="fa fa-search"></i> Search</button> <br> </div> </form> </div> </div> </div> </div> </div> </div>
You can download the complete source code of project from the Download link below.