A College Management System is a web application that simplifies academic administration by managing student records, staff information, courses, attendance, leave applications, and examination results. This project uses Django and SQLite to provide separate dashboards for the Head of Department (HOD), staff, and students.
Key Features
Application provides three role-based dashboards with different permissions and functionalities.
1. Head of Department (HOD)
- Manages student and staff accounts.
- Creates courses and subjects and assigns faculty members.
- Monitors attendance and examination results.
- Approves or rejects leave applications.
- Reviews feedback and responds to students and staff.
2. Staff
- Views assigned subjects and enrolled students.
- Records and manages student attendance.
- Adds examination results and marks.
- Submits leave applications.
- Sends feedback to the HOD.
3. Student
- Views personal and academic information.
- Checks attendance records and attendance percentage.
- Views examination results.
- Submits leave applications and tracks their status.
- Sends feedback to the administration.
Project Structure
Follow the steps below to create and run the application.
1. Creating a Virtual Environment
Create a project directory and virtual environment to isolate dependencies.
mkdir College_Management_System
cd College_Management_System
python3 -m venv .venv
source .venv/bin/activate
For Windows:
py -m venv .venv
.venv\Scripts\activate
2. Installing Django
Create a requirement.txt file in the project root directory.
Django==6.1.2
Install Django using the following command:
python -m pip install -r requirements.txt
3. Creating a Django Project and Application
Create the main Django project and an application named management.
django-admin startproject college_portal .
python manage.py startapp management
Explanation:
- startproject creates the main project configuration.
- startapp creates the application responsible for college management functionality.
- manage.py provides commands for managing the Django project.
4. Configuring Django Application
Open college_portal/settings.py and add management to INSTALLED_APPS.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'management',
]
Configure the template directory by updating the TEMPLATES setting:
'DIRS': [BASE_DIR / 'templates'],
Add following configuration at the bottom of settings.py:
AUTH_USER_MODEL = 'management.CollegeUser'
TIME_ZONE = 'Asia/Kolkata'
STATIC_URL = 'static/'
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'dashboard'
LOGOUT_REDIRECT_URL = 'login'
Explanation:
- INSTALLED_APPS registers the college management application.
- DIRS allows Django to locate project-level HTML templates.
- AUTH_USER_MODEL specifies the custom authentication model.
- LOGIN_REDIRECT_URL specifies the page displayed after successful login.
5. Creating Database Models
Create models.py inside the management application. These models store user information, academic records, attendance, examination results, leave requests, and feedback.
Add following code:
from django.db import models
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.core.validators import MinValueValidator
class CollegeUser(AbstractUser):
class Role(models.TextChoices):
HOD = "HOD", "Head of Department"
STAFF = "STAFF", "Staff"
STUDENT = "STUDENT", "Student"
role = models.CharField(
max_length=10,
choices=Role.choices,
default=Role.STUDENT
)
def __str__(self):
return self.username
class Course(models.Model):
name = models.CharField(max_length=120, unique=True)
code = models.CharField(max_length=20, unique=True)
def __str__(self):
return f"{self.name} ({self.code})"
class StaffProfile(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="staff_profile"
)
department = models.CharField(max_length=120)
def __str__(self):
return self.user.username
class StudentProfile(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="student_profile"
)
roll_number = models.CharField(max_length=30, unique=True)
course = models.ForeignKey(
Course,
on_delete=models.PROTECT,
related_name="students"
)
admission_year = models.PositiveSmallIntegerField()
def __str__(self):
return self.roll_number
class Subject(models.Model):
name = models.CharField(max_length=120)
code = models.CharField(max_length=20, unique=True)
course = models.ForeignKey(
Course,
on_delete=models.CASCADE,
related_name="subjects"
)
staff = models.ForeignKey(
StaffProfile,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="subjects"
)
def __str__(self):
return self.name
class Attendance(models.Model):
subject = models.ForeignKey(
Subject,
on_delete=models.CASCADE,
related_name="attendance_records"
)
student = models.ForeignKey(
StudentProfile,
on_delete=models.CASCADE,
related_name="attendance_records"
)
date = models.DateField()
present = models.BooleanField(default=False)
recorded_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT
)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["subject", "student", "date"],
name="unique_student_subject_attendance_per_day"
)
]
def __str__(self):
return f"{self.student} - {self.subject} - {self.date}"
class LeaveApplication(models.Model):
class Status(models.TextChoices):
PENDING = "PENDING", "Pending"
APPROVED = "APPROVED", "Approved"
REJECTED = "REJECTED", "Rejected"
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="leave_applications"
)
start_date = models.DateField()
end_date = models.DateField()
reason = models.TextField()
status = models.CharField(
max_length=10,
choices=Status.choices,
default=Status.PENDING
)
reviewed_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="reviewed_leave_applications"
)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.user.username}: {self.start_date}"
class Result(models.Model):
student = models.ForeignKey(
StudentProfile,
on_delete=models.CASCADE,
related_name="results"
)
subject = models.ForeignKey(
Subject,
on_delete=models.CASCADE,
related_name="results"
)
examination = models.CharField(max_length=120)
marks_obtained = models.DecimalField(
max_digits=6,
decimal_places=2,
validators=[MinValueValidator(0)]
)
maximum_marks = models.DecimalField(
max_digits=6,
decimal_places=2,
validators=[MinValueValidator(0)]
)
added_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT
)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.student} - {self.examination}"
class Feedback(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="feedback_messages"
)
message = models.TextField()
response = models.TextField(blank=True)
responded_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="feedback_responses"
)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"Feedback from {self.user.username}"
Explanation:
- CollegeUser extends AbstractUser to introduce HOD, staff, and student roles.
- Course and Subject store academic information.
- StudentProfile and StaffProfile maintain additional user details.
- Attendance stores daily attendance records.
- LeaveApplication maintains leave requests and approval status.
- Result stores examination marks.
- Feedback stores messages and administrative responses.
6. Creating Django Forms
Create forms.py inside the management directory. These forms handle student and staff registration, course creation, subject assignment, examination results, leave applications, and feedback.
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
from .models import (
Course, Subject, StaffProfile, StudentProfile,
Result, LeaveApplication, Feedback
)
User = get_user_model()
class StudentCreateForm(forms.Form):
username = forms.CharField(max_length=150)
first_name = forms.CharField(max_length=150)
last_name = forms.CharField(max_length=150, required=False)
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput)
roll_number = forms.CharField(max_length=30)
course = forms.ModelChoiceField(queryset=Course.objects.all())
admission_year = forms.IntegerField(min_value=2000, max_value=2100)
def clean_username(self):
value = self.cleaned_data["username"]
if User.objects.filter(username=value).exists():
raise forms.ValidationError("Username already exists.")
return value
def clean_password(self):
value = self.cleaned_data["password"]
validate_password(value)
return value
def clean_roll_number(self):
value = self.cleaned_data["roll_number"]
if StudentProfile.objects.filter(roll_number=value).exists():
raise forms.ValidationError("Roll number already exists.")
return value
class StaffCreateForm(forms.Form):
username = forms.CharField(max_length=150)
first_name = forms.CharField(max_length=150)
last_name = forms.CharField(max_length=150, required=False)
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput)
department = forms.CharField(max_length=120)
def clean_username(self):
value = self.cleaned_data["username"]
if User.objects.filter(username=value).exists():
raise forms.ValidationError("Username already exists.")
return value
def clean_password(self):
value = self.cleaned_data["password"]
validate_password(value)
return value
class CourseForm(forms.ModelForm):
class Meta:
model = Course
fields = ["name", "code"]
class SubjectForm(forms.ModelForm):
class Meta:
model = Subject
fields = ["name", "code", "course", "staff"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["staff"].queryset = (
StaffProfile.objects.select_related("user").all()
)
self.fields["staff"].required = False
class ResultForm(forms.ModelForm):
class Meta:
model = Result
fields = [
"student", "subject", "examination",
"marks_obtained", "maximum_marks"
]
widgets = {
"marks_obtained": forms.NumberInput(
attrs={"step": "0.01", "min": "0"}
),
"maximum_marks": forms.NumberInput(
attrs={"step": "0.01", "min": "0.01"}
),
}
def __init__(self, *args, staff_profile=None, **kwargs):
super().__init__(*args, **kwargs)
subjects = Subject.objects.all()
if staff_profile is not None:
subjects = subjects.filter(staff=staff_profile)
self.fields["subject"].queryset = subjects.select_related("course")
course_ids = subjects.values_list("course_id", flat=True)
self.fields["student"].queryset = StudentProfile.objects.filter(
course_id__in=course_ids
).select_related("user")
def clean(self):
cleaned = super().clean()
student = cleaned.get("student")
subject = cleaned.get("subject")
obtained = cleaned.get("marks_obtained")
maximum = cleaned.get("maximum_marks")
if student and subject and student.course_id != subject.course_id:
self.add_error(
"student",
"Student does not belong to the selected subject's course."
)
if maximum is not None and maximum <= 0:
self.add_error("maximum_marks", "Maximum marks must be positive.")
if obtained is not None and maximum is not None and obtained > maximum:
self.add_error(
"marks_obtained",
"Marks cannot exceed maximum marks."
)
return cleaned
class LeaveApplicationForm(forms.ModelForm):
class Meta:
model = LeaveApplication
fields = ["start_date", "end_date", "reason"]
widgets = {
"start_date": forms.DateInput(attrs={"type": "date"}),
"end_date": forms.DateInput(attrs={"type": "date"}),
}
def clean(self):
cleaned = super().clean()
start = cleaned.get("start_date")
end = cleaned.get("end_date")
if start and end and end < start:
self.add_error("end_date", "Invalid leave date range.")
return cleaned
class FeedbackForm(forms.ModelForm):
class Meta:
model = Feedback
fields = ["message"]
class FeedbackReplyForm(forms.ModelForm):
class Meta:
model = Feedback
fields = ["response"]
Explanation:
- StudentCreateForm and StaffCreateForm validate registration details.
- CourseForm and SubjectForm handle academic information.
- ResultForm validates examination marks and assigned subjects.
- LeaveApplicationForm checks the requested leave dates.
- FeedbackForm and FeedbackReplyForm manage communication.
7. Implementing Role-Based Access Control
Create decorators.py to restrict access to dashboard operations based on the logged-in user's role.
from functools import wraps
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
def role_required(*roles):
def decorator(view_func):
@wraps(view_func)
@login_required
def wrapper(request, *args, **kwargs):
if request.user.role not in roles:
raise PermissionDenied(
"You do not have permission to access this page."
)
return view_func(request, *args, **kwargs)
return wrapper
return decorator
Explanation:
- login_required ensures that only authenticated users access protected pages.
- role_required checks whether the user has the required role.
- PermissionDenied blocks unauthorized access.
8. Implementing Application Views
Create views.py to handle the main application operations. The views connect forms, database models, authentication, and HTML templates.
Add the following code:
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.db import transaction
from django.db.models import Count
from django.http import HttpResponseForbidden
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.dateparse import parse_date
from django.views.decorators.http import require_POST
from .decorators import role_required
from .forms import (
StudentCreateForm,
StaffCreateForm,
CourseForm,
SubjectForm,
ResultForm,
LeaveApplicationForm,
FeedbackForm,
FeedbackReplyForm,
)
from .models import (
Attendance,
Course,
Feedback,
LeaveApplication,
Result,
StaffProfile,
StudentProfile,
Subject,
)
User = get_user_model()
def dashboard(request):
if not request.user.is_authenticated:
return redirect("login")
if request.user.role == User.Role.HOD:
return redirect("hod_dashboard")
if request.user.role == User.Role.STAFF:
return redirect("staff_dashboard")
if request.user.role == User.Role.STUDENT:
return redirect("student_dashboard")
return HttpResponseForbidden("No dashboard assigned.")
# ---------------- HOD DASHBOARD ----------------
@role_required(User.Role.HOD)
def hod_dashboard(request):
context = {
"student_count": StudentProfile.objects.count(),
"staff_count": StaffProfile.objects.count(),
"course_count": Course.objects.count(),
"subject_count": Subject.objects.count(),
"pending_leaves": LeaveApplication.objects.filter(
status=LeaveApplication.Status.PENDING
).count(),
"recent_leaves": LeaveApplication.objects.select_related(
"user"
)[:5],
}
return render(
request,
"management/hod_dashboard.html",
context
)
@role_required(User.Role.HOD)
def student_list(request):
students = StudentProfile.objects.select_related(
"user", "course"
)
rows = [
(
student.roll_number,
student.user.get_full_name(),
student.user.email,
student.course.name,
student.admission_year
)
for student in students
]
return render(request, "management/records.html", {
"title": "Students",
"headers": [
"Roll Number", "Name", "Email", "Course", "Admission Year"
],
"rows": rows,
"add_url": "add_student",
})
@role_required(User.Role.HOD)
def add_student(request):
form = StudentCreateForm(request.POST or None)
if request.method == "POST" and form.is_valid():
data = form.cleaned_data
with transaction.atomic():
user = User.objects.create_user(
username=data["username"],
password=data["password"],
first_name=data["first_name"],
last_name=data["last_name"],
email=data["email"],
role=User.Role.STUDENT,
)
StudentProfile.objects.create(
user=user,
roll_number=data["roll_number"],
course=data["course"],
admission_year=data["admission_year"],
)
messages.success(request, "Student created successfully.")
return redirect("student_list")
return render(request, "management/form.html", {
"title": "Add Student",
"form": form,
})
@role_required(User.Role.HOD)
def staff_list(request):
staff = StaffProfile.objects.select_related("user")
rows = [
(
item.user.username,
item.user.get_full_name(),
item.user.email,
item.department,
item.subjects.count()
)
for item in staff
]
return render(request, "management/records.html", {
"title": "Staff",
"headers": [
"Username", "Name", "Email", "Department", "Subjects"
],
"rows": rows,
"add_url": "add_staff",
})
@role_required(User.Role.HOD)
def add_staff(request):
form = StaffCreateForm(request.POST or None)
if request.method == "POST" and form.is_valid():
data = form.cleaned_data
with transaction.atomic():
user = User.objects.create_user(
username=data["username"],
password=data["password"],
first_name=data["first_name"],
last_name=data["last_name"],
email=data["email"],
role=User.Role.STAFF,
)
StaffProfile.objects.create(
user=user,
department=data["department"]
)
messages.success(request, "Staff member created.")
return redirect("staff_list")
return render(request, "management/form.html", {
"title": "Add Staff",
"form": form,
})
@role_required(User.Role.HOD)
def course_list(request):
courses = Course.objects.annotate(
student_total=Count("students")
)
rows = [
(
course.code,
course.name,
course.student_total,
course.subjects.count()
)
for course in courses
]
return render(request, "management/records.html", {
"title": "Courses",
"headers": ["Code", "Course", "Students", "Subjects"],
"rows": rows,
"add_url": "add_course",
})
@role_required(User.Role.HOD)
def add_course(request):
form = CourseForm(request.POST or None)
if request.method == "POST" and form.is_valid():
form.save()
messages.success(request, "Course added successfully.")
return redirect("course_list")
return render(request, "management/form.html", {
"title": "Add Course",
"form": form,
})
@role_required(User.Role.HOD)
def subject_list(request):
subjects = Subject.objects.select_related(
"course", "staff__user"
)
rows = [
(
item.code,
item.name,
item.course.name,
item.staff.user.get_full_name()
if item.staff else "Not assigned"
)
for item in subjects
]
return render(request, "management/records.html", {
"title": "Subjects",
"headers": ["Code", "Subject", "Course", "Assigned Staff"],
"rows": rows,
"add_url": "add_subject",
})
@role_required(User.Role.HOD)
def add_subject(request):
form = SubjectForm(request.POST or None)
if request.method == "POST" and form.is_valid():
form.save()
messages.success(request, "Subject added successfully.")
return redirect("subject_list")
return render(request, "management/form.html", {
"title": "Add Subject",
"form": form,
})
@role_required(User.Role.HOD)
def hod_leave_list(request):
leaves = LeaveApplication.objects.select_related(
"user", "reviewed_by"
)
return render(request, "management/leave_list.html", {
"title": "Leave Applications",
"leaves": leaves,
"can_review": True,
})
@role_required(User.Role.HOD)
@require_POST
def review_leave(request, pk):
leave = get_object_or_404(LeaveApplication, pk=pk)
action = request.POST.get("action")
if leave.status != LeaveApplication.Status.PENDING:
messages.warning(request, "Application already reviewed.")
elif action == "approve":
leave.status = LeaveApplication.Status.APPROVED
leave.reviewed_by = request.user
leave.save(update_fields=["status", "reviewed_by"])
messages.success(request, "Leave approved.")
elif action == "reject":
leave.status = LeaveApplication.Status.REJECTED
leave.reviewed_by = request.user
leave.save(update_fields=["status", "reviewed_by"])
messages.success(request, "Leave rejected.")
else:
messages.error(request, "Invalid action.")
return redirect("hod_leave_list")
@role_required(User.Role.HOD)
def hod_feedback_list(request):
feedback = Feedback.objects.select_related(
"user", "responded_by"
)
return render(request, "management/feedback_list.html", {
"title": "Feedback",
"feedback_items": feedback,
})
@role_required(User.Role.HOD)
def reply_feedback(request, pk):
item = get_object_or_404(Feedback, pk=pk)
form = FeedbackReplyForm(
request.POST or None,
instance=item
)
if request.method == "POST" and form.is_valid():
saved = form.save(commit=False)
saved.responded_by = request.user
saved.save()
messages.success(request, "Feedback response saved.")
return redirect("hod_feedback_list")
return render(request, "management/form.html", {
"title": "Respond to Feedback",
"form": form,
})
@role_required(User.Role.HOD)
def hod_attendance_list(request):
records = Attendance.objects.select_related(
"student__user",
"student__course",
"subject"
)
rows = [
(
record.date,
record.student.roll_number,
record.student.user.get_full_name(),
record.student.course.name,
record.subject.name,
"Present" if record.present else "Absent"
)
for record in records
]
return render(request, "management/records.html", {
"title": "Attendance Records",
"headers": [
"Date", "Roll Number", "Student",
"Course", "Subject", "Status"
],
"rows": rows,
})
@role_required(User.Role.HOD)
def hod_result_list(request):
results = Result.objects.select_related(
"student__user",
"subject"
)
rows = [
(
item.student.roll_number,
item.student.user.get_full_name(),
item.subject.name,
item.examination,
item.marks_obtained,
item.maximum_marks
)
for item in results
]
return render(request, "management/records.html", {
"title": "Examination Results",
"headers": [
"Roll Number", "Student", "Subject",
"Examination", "Marks", "Maximum Marks"
],
"rows": rows,
})
@role_required(User.Role.STAFF)
def staff_dashboard(request):
profile = get_object_or_404(
StaffProfile,
user=request.user
)
subjects = profile.subjects.select_related("course")
context = {
"profile": profile,
"subjects": subjects,
"student_count": StudentProfile.objects.filter(
course__in=subjects.values("course_id")
).distinct().count(),
"pending_leaves": LeaveApplication.objects.filter(
user=request.user,
status=LeaveApplication.Status.PENDING
).count(),
}
return render(
request,
"management/staff_dashboard.html",
context
)
@role_required(User.Role.STAFF)
def take_attendance(request):
profile = get_object_or_404(
StaffProfile,
user=request.user
)
subjects = profile.subjects.select_related("course")
selected_subject = None
students = StudentProfile.objects.none()
selected_date = (
request.GET.get("date")
or request.POST.get("date")
or ""
)
subject_id = (
request.GET.get("subject")
or request.POST.get("subject")
)
if subject_id:
selected_subject = get_object_or_404(
subjects,
pk=subject_id
)
students = StudentProfile.objects.filter(
course=selected_subject.course
).select_related("user")
if request.method == "POST":
attendance_date = parse_date(selected_date)
if not selected_subject or not attendance_date:
messages.error(request, "Select a valid subject and date.")
else:
present_ids = set(
request.POST.getlist("present_students")
)
with transaction.atomic():
for student in students:
Attendance.objects.update_or_create(
subject=selected_subject,
student=student,
date=attendance_date,
defaults={
"present": str(student.pk) in present_ids,
"recorded_by": request.user,
},
)
messages.success(request, "Attendance saved.")
return redirect(
f"/staff/attendance/?subject={selected_subject.pk}"
f"&date={attendance_date.isoformat()}"
)
return render(request, "management/take_attendance.html", {
"subjects": subjects,
"selected_subject": selected_subject,
"students": students,
"selected_date": selected_date,
})
@role_required(User.Role.STAFF)
def add_result(request):
profile = get_object_or_404(
StaffProfile,
user=request.user
)
form = ResultForm(
request.POST or None,
staff_profile=profile
)
if request.method == "POST" and form.is_valid():
result = form.save(commit=False)
result.added_by = request.user
result.save()
messages.success(request, "Result saved successfully.")
return redirect("staff_dashboard")
return render(request, "management/form.html", {
"title": "Add Examination Result",
"form": form,
"help_text": "Select a subject and a student enrolled in its course.",
})
@role_required(User.Role.STAFF)
def staff_leave(request):
form = LeaveApplicationForm(request.POST or None)
if request.method == "POST" and form.is_valid():
leave = form.save(commit=False)
leave.user = request.user
leave.save()
messages.success(request, "Leave application submitted.")
return redirect("staff_dashboard")
return render(request, "management/form.html", {
"title": "Apply for Leave",
"form": form,
})
@role_required(User.Role.STAFF)
def staff_feedback(request):
form = FeedbackForm(request.POST or None)
if request.method == "POST" and form.is_valid():
item = form.save(commit=False)
item.user = request.user
item.save()
messages.success(request, "Feedback submitted.")
return redirect("staff_dashboard")
return render(request, "management/form.html", {
"title": "Send Feedback",
"form": form,
})
@role_required(User.Role.STUDENT)
def student_dashboard(request):
profile = get_object_or_404(
StudentProfile,
user=request.user
)
attendance = Attendance.objects.filter(student=profile)
total = attendance.count()
present = attendance.filter(present=True).count()
percentage = (
round((present / total) * 100, 1)
if total else 0
)
context = {
"profile": profile,
"total_attendance": total,
"present_count": present,
"attendance_percentage": percentage,
"result_count": Result.objects.filter(
student=profile
).count(),
"pending_leaves": LeaveApplication.objects.filter(
user=request.user,
status=LeaveApplication.Status.PENDING
).count(),
}
return render(
request,
"management/student_dashboard.html",
context
)
@role_required(User.Role.STUDENT)
def student_attendance(request):
profile = get_object_or_404(
StudentProfile,
user=request.user
)
records = Attendance.objects.filter(
student=profile
).select_related("subject").order_by("-date")
rows = [
(
item.date,
item.subject.name,
"Present" if item.present else "Absent"
)
for item in records
]
return render(request, "management/records.html", {
"title": "My Attendance",
"headers": ["Date", "Subject", "Status"],
"rows": rows,
})
@role_required(User.Role.STUDENT)
def student_results(request):
profile = get_object_or_404(
StudentProfile,
user=request.user
)
results = Result.objects.filter(
student=profile
).select_related("subject")
rows = [
(
item.subject.name,
item.examination,
item.marks_obtained,
item.maximum_marks
)
for item in results
]
return render(request, "management/records.html", {
"title": "My Examination Results",
"headers": [
"Subject", "Examination", "Marks Obtained", "Maximum Marks"
],
"rows": rows,
})
@role_required(User.Role.STUDENT)
def student_leave(request):
form = LeaveApplicationForm(request.POST or None)
if request.method == "POST" and form.is_valid():
leave = form.save(commit=False)
leave.user = request.user
leave.save()
messages.success(request, "Leave application submitted.")
return redirect("student_dashboard")
return render(request, "management/form.html", {
"title": "Apply for Leave",
"form": form,
})
@role_required(User.Role.STUDENT)
def student_feedback(request):
form = FeedbackForm(request.POST or None)
if request.method == "POST" and form.is_valid():
item = form.save(commit=False)
item.user = request.user
item.save()
messages.success(request, "Feedback submitted.")
return redirect("student_dashboard")
return render(request, "management/form.html", {
"title": "Send Feedback",
"form": form,
})
@role_required(User.Role.STUDENT)
def student_leave_history(request):
leaves = LeaveApplication.objects.filter(
user=request.user
)
return render(request, "management/leave_list.html", {
"title": "My Leave Applications",
"leaves": leaves,
"can_review": False,
})
Explanation:
- dashboard redirects users to their respective dashboards based on their roles.
- HOD views manage student accounts, staff accounts, courses, subjects, attendance, results, leave, and feedback.
- Staff views handle assigned subjects, attendance, results, leave, and feedback.
- Student views display academic information and handle leave and feedback submissions.
- transaction.atomic() ensures that related database operations are completed together.
- update_or_create() updates existing attendance records or creates new ones.
9. Configuring Application URLs
Create urls.py inside the management application to connect the application views with their respective URLs.
from django.contrib.auth import views as auth_views
from django.urls import path
from . import views
urlpatterns = [
path(
"login/",
auth_views.LoginView.as_view(
template_name="registration/login.html"
),
name="login"
),
path(
"logout/",
auth_views.LogoutView.as_view(),
name="logout"
),
path("", views.dashboard, name="dashboard"),
# HOD URLs
path("hod/", views.hod_dashboard, name="hod_dashboard"),
path("hod/students/", views.student_list, name="student_list"),
path("hod/students/add/", views.add_student, name="add_student"),
path("hod/staff/", views.staff_list, name="staff_list"),
path("hod/staff/add/", views.add_staff, name="add_staff"),
path("hod/courses/", views.course_list, name="course_list"),
path("hod/courses/add/", views.add_course, name="add_course"),
path("hod/subjects/", views.subject_list, name="subject_list"),
path("hod/subjects/add/", views.add_subject, name="add_subject"),
path("hod/leaves/", views.hod_leave_list, name="hod_leave_list"),
path(
"hod/leaves/<int:pk>/review/",
views.review_leave,
name="review_leave"
),
path("hod/feedback/", views.hod_feedback_list, name="hod_feedback_list"),
path(
"hod/feedback/<int:pk>/reply/",
views.reply_feedback,
name="reply_feedback"
),
path(
"hod/attendance/",
views.hod_attendance_list,
name="hod_attendance_list"
),
path("hod/results/", views.hod_result_list, name="hod_result_list"),
# Staff URLs
path("staff/", views.staff_dashboard, name="staff_dashboard"),
path(
"staff/attendance/",
views.take_attendance,
name="take_attendance"
),
path("staff/results/add/", views.add_result, name="add_result"),
path("staff/leave/", views.staff_leave, name="staff_leave"),
path("staff/feedback/", views.staff_feedback, name="staff_feedback"),
# Student URLs
path("student/", views.student_dashboard, name="student_dashboard"),
path(
"student/attendance/",
views.student_attendance,
name="student_attendance"
),
path("student/results/", views.student_results, name="student_results"),
path("student/leave/", views.student_leave, name="student_leave"),
path(
"student/leave/history/",
views.student_leave_history,
name="student_leave_history"
),
path(
"student/feedback/",
views.student_feedback,
name="student_feedback"
),
]
Explanation:
- path() connects URL patterns with their corresponding views.
- LoginView and LogoutView provide authentication endpoints.
- Separate URL groups maintain HOD, staff, and student functionality.
- Named URLs simplify redirects and navigation.
Now open college_portal/urls.py and connect the application routes.
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("management.urls")),
]
Explanation:
- Admin route provides access to Django's administration panel.
- include() connects the application-level URL configuration with the main project.
10. Registering Models in Django Admin
Open admin.py inside the management application. Register the models to manage college records through the Django administration interface.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import (
CollegeUser,
Course,
StaffProfile,
StudentProfile,
Subject,
Attendance,
LeaveApplication,
Result,
Feedback,
)
@admin.register(CollegeUser)
class CollegeUserAdmin(UserAdmin):
fieldsets = UserAdmin.fieldsets + (
("College Role", {"fields": ("role",)}),
)
add_fieldsets = UserAdmin.add_fieldsets + (
("College Role", {"fields": ("role",)}),
)
list_display = (
"username",
"email",
"first_name",
"last_name",
"role",
"is_active",
)
list_filter = ("role", "is_active", "is_staff")
admin.site.register([
Course,
StaffProfile,
StudentProfile,
Subject,
Attendance,
LeaveApplication,
Result,
Feedback,
])
Explanation:
- CollegeUserAdmin extends Django's default user administration interface.
- list_display specifies the columns displayed in the admin panel.
- list_filter allows administrators to filter accounts by role and status.
- admin.site.register() makes the remaining models available for management.
11. Create HTML Templates
Create a templates directory in the project root. Inside it, create registration and management directories. Application uses a common base template to maintain navigation, styling, and messages across all dashboards.
1. base.html : Create templates/base.html and add the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}College Management System{% endblock %}</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f4f6fa;
color: #1f2937;
}
header {
background: #172554;
color: white;
padding: 18px 5%;
display: flex;
justify-content: space-between;
}
header a {
color: white;
text-decoration: none;
}
main {
max-width: 1100px;
margin: 30px auto;
padding: 0 18px;
}
.nav {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 22px;
}
.nav a, .btn {
background: #1d4ed8;
color: white;
padding: 10px 14px;
border: none;
border-radius: 6px;
text-decoration: none;
cursor: pointer;
display: inline-block;
}
.secondary {
background: #475569;
}
.danger {
background: #b91c1c;
}
.card {
background: white;
border: 1px solid #e5e7eb;
border-radius: 10px;
padding: 20px;
margin-bottom: 18px;
}
.grid {
display: grid;
grid-template-columns: repeat(
auto-fit, minmax(180px, 1fr)
);
gap: 14px;
}
.metric {
font-size: 28px;
font-weight: bold;
color: #1d4ed8;
margin-top: 8px;
}
table {
width: 100%;
border-collapse: collapse;
background: white;
}
th, td {
padding: 12px;
border-bottom: 1px solid #e5e7eb;
text-align: left;
}
th {
background: #eff6ff;
}
input, select, textarea {
width: 100%;
max-width: 560px;
padding: 10px;
border: 1px solid #cbd5e1;
border-radius: 6px;
}
input[type="checkbox"] {
width: auto;
}
textarea {
min-height: 100px;
}
.messages {
list-style: none;
padding: 0;
}
.messages li {
background: #e0f2fe;
padding: 12px;
margin-bottom: 8px;
border-radius: 6px;
}
.muted {
color: #64748b;
}
.inline {
display: inline-block;
}
@media(max-width: 600px) {
header {
flex-direction: column;
gap: 12px;
}
table {
font-size: 13px;
}
}
</style>
</head>
<body>
<header>
<div>
<a href="{% url 'dashboard' %}">
<strong>College Management System</strong>
</a>
</div>
<div>
{% if request.user.is_authenticated %}
{{ request.user.get_full_name|default:request.user.username }}
<form class="inline" method="post"
action="{% url 'logout' %}">
{% csrf_token %}
<button class="btn secondary" type="submit">
Logout
</button>
</form>
{% else %}
<a href="{% url 'login' %}">Login</a>
{% endif %}
</div>
</header>
<main>
{% if request.user.is_authenticated %}
<nav class="nav">
{% if request.user.role == "HOD" %}
<a href="{% url 'hod_dashboard' %}">Dashboard</a>
<a href="{% url 'student_list' %}">Students</a>
<a href="{% url 'staff_list' %}">Staff</a>
<a href="{% url 'course_list' %}">Courses</a>
<a href="{% url 'subject_list' %}">Subjects</a>
<a href="{% url 'hod_leave_list' %}">Leave</a>
<a href="{% url 'hod_feedback_list' %}">Feedback</a>
<a href="{% url 'hod_attendance_list' %}">Attendance</a>
<a href="{% url 'hod_result_list' %}">Results</a>
{% elif request.user.role == "STAFF" %}
<a href="{% url 'staff_dashboard' %}">Dashboard</a>
<a href="{% url 'take_attendance' %}">Attendance</a>
<a href="{% url 'add_result' %}">Add Result</a>
<a href="{% url 'staff_leave' %}">Apply Leave</a>
<a href="{% url 'staff_feedback' %}">Feedback</a>
{% elif request.user.role == "STUDENT" %}
<a href="{% url 'student_dashboard' %}">Dashboard</a>
<a href="{% url 'student_attendance' %}">Attendance</a>
<a href="{% url 'student_results' %}">Results</a>
<a href="{% url 'student_leave' %}">Apply Leave</a>
<a href="{% url 'student_leave_history' %}">Leave History</a>
<a href="{% url 'student_feedback' %}">Feedback</a>
{% endif %}
</nav>
{% endif %}
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% block content %}
{% endblock %}
</main>
</body>
</html>
Explanation:
- base.html provides the shared layout for all application pages.
- Navigation menu changes according to the logged-in user's role.
- Django template inheritance allows individual pages to reuse the same layout.
- Messages display the results of operations such as creating records or submitting leave requests.
2. Login Page: Create templates/registeration/login.html.
{% extends "base.html" %}
{% block title %}
Login - College Management System
{% endblock %}
{% block content %}
<div class="card">
<h1>Login</h1>
<p class="muted">
Enter your registered username and password.
</p>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button class="btn" type="submit">
Login
</button>
</form>
</div>
{% endblock %}
Explanation:
- LoginView provides Django's authentication functionality.
- form.as_p displays the username and password fields.
- csrf_token protects the submitted form against CSRF attacks.
3. HOD Dashboard: Create templates/management/hod_dashboard.html
{% extends "base.html" %}
{% block title %}
HOD Dashboard
{% endblock %}
{% block content %}
<h1>HOD Dashboard</h1>
<p class="muted">
Manage academic records and college activities.
</p>
<div class="grid">
<div class="card">
Students
<div class="metric">{{ student_count }}</div>
</div>
<div class="card">
Staff
<div class="metric">{{ staff_count }}</div>
</div>
<div class="card">
Courses
<div class="metric">{{ course_count }}</div>
</div>
<div class="card">
Subjects
<div class="metric">{{ subject_count }}</div>
</div>
<div class="card">
Pending Leave Requests
<div class="metric">{{ pending_leaves }}</div>
</div>
</div>
<div class="card">
<h2>Recent Leave Applications</h2>
{% for leave in recent_leaves %}
<p>
{{ leave.user.username }} -
{{ leave.start_date }} to {{ leave.end_date }}
- {{ leave.get_status_display }}
</p>
{% empty %}
<p>No leave applications found.</p>
{% endfor %}
<a href="{% url 'hod_leave_list' %}" class="btn">
Review Applications
</a>
</div>
{% endblock %}
Explanation:
- Displays student, staff, course, and subject counts.
- Shows pending leave applications.
- Provides access to administrative operations.
4. Staff Dashboard: Create templates/management/staff_dashboard.html
{% extends "base.html" %}
{% block title %}
Staff Dashboard
{% endblock %}
{% block content %}
<h1>Staff Dashboard</h1>
<div class="card">
<p>
<strong>Department:</strong>
{{ profile.department }}
</p>
<p>
<strong>Students:</strong>
{{ student_count }}
</p>
<p>
<strong>Pending Leave:</strong>
{{ pending_leaves }}
</p>
</div>
<div class="card">
<h2>Assigned Subjects</h2>
<table>
<thead>
<tr>
<th>Subject</th>
<th>Code</th>
<th>Course</th>
</tr>
</thead>
<tbody>
{% for subject in subjects %}
<tr>
<td>{{ subject.name }}</td>
<td>{{ subject.code }}</td>
<td>{{ subject.course.name }}</td>
</tr>
{% empty %}
<tr>
<td colspan="3">No subjects assigned.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
Explanation:
- Displays staff department and student statistics.
- Lists subjects assigned by the HOD.
- Provides access to attendance, results, and leave operations through navigation.
5. Student Dashboard: Create templates/management/student_dashboard.html.
{% extends "base.html" %}
{% block title %}
Student Dashboard
{% endblock %}
{% block content %}
<h1>Student Dashboard</h1>
<div class="card">
<p>
<strong>Name:</strong>
{{ request.user.get_full_name }}
</p>
<p>
<strong>Roll Number:</strong>
{{ profile.roll_number }}
</p>
<p>
<strong>Course:</strong>
{{ profile.course.name }}
</p>
<p>
<strong>Admission Year:</strong>
{{ profile.admission_year }}
</p>
</div>
<div class="grid">
<div class="card">
Total Attendance
<div class="metric">{{ total_attendance }}</div>
</div>
<div class="card">
Present
<div class="metric">{{ present_count }}</div>
</div>
<div class="card">
Attendance Percentage
<div class="metric">{{ attendance_percentage }}%</div>
</div>
<div class="card">
Results Published
<div class="metric">{{ result_count }}</div>
</div>
<div class="card">
Pending Leave
<div class="metric">{{ pending_leaves }}</div>
</div>
</div>
{% endblock %}
Explanation:
- Displays student profile and academic information.
- Calculates attendance statistics through the corresponding view.
- Shows the number of published examination results and pending leave requests.
12. Creating Generic Form Template
Several application operations use reusable templates rather than separate HTML files.
1. Generic Form Template: Create templates/management/form.html
{% extends "base.html" %}
{% block content %}
<div class="card">
<h1>{{ title }}</h1>
{% if help_text %}
<p class="muted">{{ help_text }}</p>
{% endif %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button class="btn" type="submit">
Save
</button>
</form>
</div>
{% endblock %}
Explanation:
- Displays forms for student, staff, course, subject, leave, and feedback operations.
- form.as_p renders the fields and validation errors.
- Same template reduces repeated HTML code.
2. Create Records Template: Create templates/management/records.html.
{% extends "base.html" %}
{% block content %}
<div class="card">
<h1>{{ title }}</h1>
{% if add_url %}
<a href="{% url add_url %}" class="btn">
Add New
</a>
{% endif %}
<div style="overflow-x:auto">
<table>
<thead>
<tr>
{% for heading in headers %}
<th>{{ heading }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in rows %}
<tr>
{% for value in row %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% empty %}
<tr>
<td colspan="20">No records found.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
Explanation:
- Displays students, staff, courses, subjects, attendance, and results.
- Dynamically generates table headings and rows.
- Displays an Add New option when the corresponding view supports record creation.
3. Attendance Template: Create templates/management/take_attendance.html.
{% extends "base.html" %}
{% block content %}
<div class="card">
<h1>Take Attendance</h1>
<form method="get">
<p>
<label>Subject</label>
<select name="subject" required>
<option value="">Select Subject</option>
{% for subject in subjects %}
<option value="{{ subject.pk }}"
{% if selected_subject and selected_subject.pk == subject.pk %}
selected
{% endif %}>
{{ subject.name }} - {{ subject.course.name }}
</option>
{% endfor %}
</select>
</p>
<p>
<label>Date</label>
<input
type="date"
name="date"
value="{{ selected_date }}"
required
>
</p>
<button class="btn" type="submit">
Load Students
</button>
</form>
</div>
{% if selected_subject %}
<div class="card">
<h2>{{ selected_subject.name }}</h2>
<p class="muted">
Select the students who are present.
Unchecked students will be marked absent.
</p>
<form method="post">
{% csrf_token %}
<input
type="hidden"
name="subject"
value="{{ selected_subject.pk }}"
>
<input
type="hidden"
name="date"
value="{{ selected_date }}"
>
<table>
<thead>
<tr>
<th>Roll Number</th>
<th>Student</th>
<th>Present</th>
</tr>
</thead>
<tbody>
{% for student in students %}
<tr>
<td>{{ student.roll_number }}</td>
<td>{{ student.user.get_full_name }}</td>
<td>
<input
type="checkbox"
name="present_students"
value="{{ student.pk }}"
>
</td>
</tr>
{% empty %}
<tr>
<td colspan="3">No enrolled students found.</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if students %}
<br>
<button class="btn" type="submit">
Save Attendance
</button>
{% endif %}
</form>
</div>
{% endif %}
{% endblock %}
Explanation:
- Staff member selects a subject and attendance date.
- Application retrieves students enrolled in the selected course.
- Checkboxes identify present students.
- Unchecked students are recorded as absent.
4. Leave Application Template: Create templates/management/leave_list.html.
{% extends "base.html" %}
{% block content %}
<div class="card">
<h1>{{ title }}</h1>
<table>
<thead>
<tr>
<th>User</th>
<th>Dates</th>
<th>Reason</th>
<th>Status</th>
{% if can_review %}
<th>Action</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for leave in leaves %}
<tr>
<td>
{{ leave.user.username }}
</td>
<td>
{{ leave.start_date }} to {{ leave.end_date }}
</td>
<td>
{{ leave.reason }}
</td>
<td>
{{ leave.get_status_display }}
</td>
{% if can_review %}
<td>
{% if leave.status == "PENDING" %}
<form
method="post"
action="{% url 'review_leave' leave.pk %}"
>
{% csrf_token %}
<button
class="btn"
name="action"
value="approve"
>
Approve
</button>
<button
class="btn danger"
name="action"
value="reject"
>
Reject
</button>
</form>
{% else %}
Reviewed
{% endif %}
</td>
{% endif %}
</tr>
{% empty %}
<tr>
<td colspan="5">No leave applications found.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
Explanation:
- Displays leave applications and their current status.
- HOD can approve or reject pending requests.
- Students can view their own leave history.
5. Feedback Template: Create templates/management/feedback_list.html.
{% extends "base.html" %}
{% block content %}
<div class="card">
<h1>{{ title }}</h1>
{% for item in feedback_items %}
<div class="card">
<p>
<strong>{{ item.user.username }}</strong>
- {{ item.created_at }}
</p>
<p>
{{ item.message }}
</p>
<p>
<strong>Response:</strong>
{{ item.response|default:"Not answered yet" }}
</p>
<a
href="{% url 'reply_feedback' item.pk %}"
class="btn"
>
Reply
</a>
</div>
{% empty %}
<p>No feedback submitted yet.</p>
{% endfor %}
</div>
{% endblock %}
Explanation:
- Displays feedback submitted by students and staff.
- HOD can open individual messages and submit responses.
- Responses are stored in the Feedback model.
13. Creating Demonstration Accounts and Sample Data
Create the following directory structure inside management:
Create seed_demo.py to automatically populate the database with sample users and academic records.
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from django.utils import timezone
from management.models import (
Course,
StaffProfile,
StudentProfile,
Subject,
Attendance,
Result,
)
User = get_user_model()
class Command(BaseCommand):
help = "Create sample college management data."
def handle(self, *args, **options):
course, _ = Course.objects.get_or_create(
code="BCA",
defaults={
"name": "Bachelor of Computer Applications"
}
)
hod, created = User.objects.get_or_create(
username="hod",
defaults={
"first_name": "Harsh",
"last_name": "Mehta",
"email": "hod@example.com",
"role": User.Role.HOD,
}
)
if created:
hod.set_password("HOD@12345")
hod.save()
else:
hod.role = User.Role.HOD
hod.save(update_fields=["role"])
staff, created = User.objects.get_or_create(
username="staff1",
defaults={
"first_name": "Asha",
"last_name": "Sharma",
"email": "asha@example.com",
"role": User.Role.STAFF,
}
)
if created:
staff.set_password("Staff@12345")
staff.save()
else:
staff.role = User.Role.STAFF
staff.save(update_fields=["role"])
staff_profile, _ = StaffProfile.objects.get_or_create(
user=staff,
defaults={"department": "Computer Science"}
)
student, created = User.objects.get_or_create(
username="student1",
defaults={
"first_name": "Riya",
"last_name": "Verma",
"email": "riya@example.com",
"role": User.Role.STUDENT,
}
)
if created:
student.set_password("Student@12345")
student.save()
else:
student.role = User.Role.STUDENT
student.save(update_fields=["role"])
student_profile, _ = StudentProfile.objects.get_or_create(
user=student,
defaults={
"roll_number": "BCA001",
"course": course,
"admission_year": timezone.localdate().year,
}
)
subject, _ = Subject.objects.get_or_create(
code="BCA101",
defaults={
"name": "Programming Fundamentals",
"course": course,
"staff": staff_profile,
}
)
if subject.staff_id is None:
subject.staff = staff_profile
subject.save(update_fields=["staff"])
Attendance.objects.get_or_create(
subject=subject,
student=student_profile,
date=timezone.localdate(),
defaults={
"present": True,
"recorded_by": staff,
}
)
Result.objects.get_or_create(
student=student_profile,
subject=subject,
examination="Unit Test 1",
defaults={
"marks_obtained": 42,
"maximum_marks": 50,
"added_by": staff,
}
)
self.stdout.write(
self.style.SUCCESS("Demo data created successfully.")
)
self.stdout.write("HOD: hod / HOD@12345")
self.stdout.write("Staff: staff1 / Staff@12345")
self.stdout.write("Student: student1 / Student@12345")
Explanation:
- Creates one HOD, staff member, and student account.
- Creates a sample BCA course and Programming Fundamentals subject.
- Assigns the subject to the staff member.
- Adds sample attendance and examination result records.
- get_or_create() prevents duplicate sample records when the command is executed again.
14. Creating Database Tables
After creating the models, generate and apply database migrations.
Run the commands from the direcotry containing manage.py.
python manage.py makemigrations management
python manage.py migrate
Explanation:
- makemigrations generates migration files based on model changes.
- migrate applies migrations and creates the required SQLite database tables.
15. Generate Sample Data and Run Application
Execute the custom management command to populate the database.
python manage.py seed_demo
Start Django Development server.
python manage.py runserver
Project Output
Final application provides separate interfaces for HOD, staff, and students.
1. HOD Dashboard
Displays student and staff counts, academic information, pending leave requests, attendance records, and examination results.

2. Staff Dashboard
Displays assigned subjects and provides access to attendance recording, examination results, leave applications, and feedback.

3. Student Dashboard
Displays student information, attendance percentage, examination results, and leave application status.
