Building Company Dashboard Permissions and Secure Resume Access in Django Admin
Today marks an important milestone in the project—the student-side MVP of my Placement Portal is complete.
I have now shifted my focus to the company dashboard and started customizing Django Admin to behave more like a role-specific dashboard rather than a generic administration panel.
The implementation currently focuses on three major areas:
- User permissions
- Queryset customization
- Special business-specific actions
User Permissions
Company users should not have unrestricted access to the entire administration panel.
Their responsibilities are intentionally limited to:
- Creating job posts
- Viewing job posts
- Updating their own job posts
- Deleting their own job posts
- Viewing applications submitted for their jobs
Initially, I considered assigning permissions individually whenever a new company user is created.
However, that approach does not scale.
Instead, I decided to manage permissions using groups.
The idea is simple:
- Create a Company group programmatically.
- Assign all required permissions to the group.
- Whenever a new company user is created, simply assign them to the Company group.
This significantly reduces permission management overhead and makes future changes easier.
Queryset Customization
Permissions alone are not sufficient.
Even if a company user has permission to view job posts, they should only be able to see their own records.
For example, if multiple companies are using the portal:
- Company A should only see its job posts and applications.
- Company B should only see its job posts and applications.
To achieve this, I customized the queryset returned by Django Admin.
The queryset is filtered according to the currently logged-in company user.
This provides two benefits:
- Improved user experience.
- Better data isolation between companies.
The user simply sees the records relevant to them without being exposed to data belonging to other organizations.
Secure Resume Access
Resume files are arguably one of the most sensitive pieces of data stored within a placement portal.
They contain information such as:
- Contact details
- Educational information
- Professional experience
- Skills and projects
Because of this, I intentionally avoided exposing media file URLs directly.
Instead, I implemented secure resume access using three components.
1. View Resume Action
A "View Resume" link is displayed alongside applications in Django Admin.
The link itself does not point directly to the media file.
2. Protected API View
I created a dedicated API View responsible for serving resume files.
The API View performs the following checks:
- Is the user authenticated?
- Does the application exist?
- Is there a resume associated with the application?
- Is the user authorized to access that application?
Only after these validations pass is the resume file returned.
3. URL Routing
Finally, I registered a dedicated URL pattern for the resume endpoint.
Whenever a company user clicks the "View Resume" link:
- Django matches the URL pattern.
- The API View executes the business logic.
- The file is returned only if authorization succeeds.
This approach provides significantly better security compared to exposing media URLs directly.
Lessons Learned
Working on the company dashboard reminded me that building business software is rarely about creating beautiful interfaces alone.
A good dashboard must answer three important questions:
- What is the user allowed to do?
- What data is the user allowed to see?
- What actions require additional authorization?
Permissions, queryset filtering, and secure file serving are three small but essential building blocks for answering those questions.
The student-side MVP may be complete, but the company-side architecture is where role-based access control and secure business workflows start becoming truly interesting.
I'm documenting every engineering and architectural decision while building this Placement Portal in public.