Browse Templates/Student Portal

Student Portal

A complete student portal database schema designed for universities and colleges. Includes student profiles, course registration, assignment management with file submissions, grade tracking, and a campus announcement system. Suitable for building student information systems, university portals, or academic management platforms.

PostgreSQL10 tablesEducation
student-portalassignmentssubmissionsgradesuniversityeducation
Created with ERD Studio

About this schema

The enrollment is what a final grade belongs to. grades keys on enrollment_id rather than on the student and course separately, so retaking a course produces a second enrollment with its own grade — and a transcript can show both attempts instead of one overwriting the other.

course_prerequisites is a self-join on courses with a min_grade. That single column is what turns a prerequisite from a checkbox into a rule: it is not enough to have taken the course, you must have passed it at a stated level, and the check runs against the grade row of a previous enrollment.

Assignments carry weight_pct alongside max_score, which means the course's grading scheme lives in the data rather than in a spreadsheet. A submission scores against max_score and contributes according to its weight, so a final mark is computable from rows instead of from someone's arithmetic.

Key relationships

students → enrollments → grades
The final mark belongs to one registration, which is what lets a repeated course keep both attempts on the transcript.
courses → assignments → submissions
The assignment states max_score and weight_pct; the submission records the score, the feedback and whether it was late.
courses → course_prerequisites (course_id, prerequisite_id)
A self-join with min_grade, so eligibility is a rule about a previous grade rather than a yes-or-no flag.
departments → students / courses / events
The organisational unit that owns people, curriculum and the calendar, keeping each scoped to a faculty.
grades.score / grade_letter / gpa_points
Three representations of one result, stored together because each is used by a different consumer and the scale can change.
announcements.course_id
Nullable, so campus-wide notices and course notices share one table and one query with a priority column to rank them.

Design decisions

gpa_points is stored, not derived

Converting a letter to points is a formula, and the formula changes — institutions revise their scales and apply the new one only going forward. Storing gpa_points freezes the conversion used at the time, so a transcript from four years ago still computes the GPA the student was actually awarded. Deriving it would silently restate every past result the day the scale is edited.

students.gpa is a cached aggregate

It can be recomputed from grades and gpa_points at any time, and it is kept on the row because a portal shows it on every screen. That makes it a cache with the usual obligation: it must be recalculated when a grade is corrected, and it is never the figure a dispute is settled with — the grade rows are.

is_late is recorded on the submission

Comparing submitted_at to the assignment's due_date gives the answer, and it gives the wrong answer as soon as an extension is granted. A stored flag records the decision that was actually made about this submission, including when a professor chose to accept it. The timestamps remain for evidence; the flag is the judgement.

Prerequisites are rows with a minimum grade

A list of required course ids would be simpler and could not express the common case — you need this course with at least a C. min_grade on the link makes the rule complete. What it still cannot express is alternatives, where either of two courses qualifies; that needs a group concept, and this schema deliberately stops short of it.

Tables in this template

departmentsDepartments
5 cols
studentsStudents
9 cols
coursesCourses
9 cols
course_prerequisitesCourse Prerequisites
4 cols
enrollmentsEnrollments
5 cols
assignmentsAssignments
8 cols
submissionsSubmissions
9 cols
gradesGrades
6 cols
announcementsAnnouncements
7 cols
eventsEvents
8 cols

Frequently asked

How do I check whether a student can enroll?
For each course_prerequisites row, find that student's enrollment in the prerequisite course and compare its grade against min_grade. A missing enrollment or a lower grade fails the check.
Why do grades hang off enrollments instead of students?
So a repeated course has two separate results. Keying on the student and course would force the second attempt to overwrite the first, which destroys exactly what a transcript is for.
How is a course's running grade calculated?
Weight each submission's score against its assignment's max_score and weight_pct, and sum. That is why both columns are on the assignment — the scheme is data, so the portal can show a provisional mark before the final grade is entered.
Why is professor a text column on courses?
Because this schema models the student's view, where the instructor is a name to display. A faculty table is needed the moment professors log in, submit grades or own their own course list — which is the first thing to add for a full system.
Can a student submit an assignment twice?
The schema allows multiple submissions rows for the same assignment and student. Whether the latest counts or only one is permitted is an application rule — a unique constraint is what enforces single-submission, and keeping several is what supports resubmission after feedback.