Friday, December 19, 2025

Develop a job Portal with Python using Django

 Specification Sheet for Web Application Development with Python
Level-4


Job 01: Develop a job Portal 
Develop a job portal using Django. In this project you have to incorporate multiple recruiters and multiple job seekers from different domains on one platform. A single recruiter can post multiple job openings using his/her account. A single job seeker can apply for multiple openings based on his skills. Both recruiters and job seekers are able to manage their account using a profile manager. 


Job Specification Information:
1. Create a project named Name_ID_JobPortal.
2. Develop a registration page using the following fields
    (Username, Display name, Email, Password, Confirm Password, User type)
3. Develop a login page using the following fields
    (Username and Password)
4. Develop a profile creation page based on user type a. Recruiters
    (Company information) b. Jobseekers (Skills set and resume upload option)
5. Develop a job posting page for recruiters
    (Title, Number of openings, Category, Job description, Skills set)
6. Develop a job applying page for jobseeker (Search)
7. Develop a skill matching page for both recruiters and jobseeker (Dashboard for skill matched job)

Job Specification Information:
1. Create a Django Project. (Naming Convention: Name_ID_Project)
2. Create a Database.
3. Store the Database.

Models.py
from django.db import models
from django.contrib.auth.models import User


class UserProfile(models.Model):
    USER_TYPE_CHOICES = [
        ("recruiter", "Recruiter"),
        ("jobseeker", "Jobseeker"),
    ]
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    display_name = models.CharField(max_length=100)
    user_type = models.CharField(max_length=10, choices=USER_TYPE_CHOICES)
    # For recruiters
    company_name = models.CharField(max_length=100, blank=True, null=True)
    company_description = models.TextField(blank=True, null=True)
    # For jobseekers
    skills = models.TextField(blank=True, null=True)  # Comma-separated skills
    resume = models.FileField(upload_to="resumes/", blank=True, null=True)

    def __str__(self):
        return f"{self.user.username} - {self.user_type}"


class Job(models.Model):
    CATEGORY_CHOICES = [
        ("tech", "Technology"),
        ("finance", "Finance"),
        ("healthcare", "Healthcare"),
        ("education", "Education"),
        ("other", "Other"),
    ]
    title = models.CharField(max_length=200)
    recruiter = models.ForeignKey(User, on_delete=models.CASCADE, related_name="jobs")
    number_of_openings = models.PositiveIntegerField()
    category = models.CharField(max_length=20, choices=CATEGORY_CHOICES)
    description = models.TextField()
    skills_required = models.TextField()  # Comma-separated skills
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title


class Application(models.Model):
    STATUS_CHOICES = [
        ("pending", "Pending"),
        ("shortlisted", "Shortlisted"),
        ("selected", "Selected"),
        ("rejected", "Rejected"),
    ]
    jobseeker = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="applications"
    )
    job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name="applications")
    applied_at = models.DateTimeField(auto_now_add=True)
    status = models.CharField(
        max_length=20, choices=STATUS_CHOICES, default="pending"
    )
    # Store jobseeker details at time of application
    skills = models.TextField(blank=True, null=True)  # Jobseeker's skills
    resume = models.FileField(upload_to="application_resumes/", blank=True, null=True)  # Jobseeker's resume
    cover_letter = models.TextField(blank=True, null=True)  # Optional cover letter

    class Meta:
        unique_together = ("jobseeker", "job")

    def __str__(self):
        return f"{self.jobseeker.username} applied for {self.job.title}"


forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import UserProfile, Job, Application

class UserRegisterForm(UserCreationForm):
    email = forms.EmailField()
    display_name = forms.CharField(max_length=100)
    user_type = forms.ChoiceField(choices=[('recruiter', 'Recruiter'), ('jobseeker', 'Jobseeker')])

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']

class UserProfileForm(forms.ModelForm):
    class Meta:
        model = UserProfile
        fields = ['display_name', 'user_type', 'company_name', 'company_description', 'skills', 'resume']
        widgets = {
            'company_description': forms.Textarea(attrs={'rows': 3}),
            'skills': forms.Textarea(attrs={'rows': 3}),
        }

class JobForm(forms.ModelForm):
    class Meta:
        model = Job
        fields = ['title', 'number_of_openings', 'category', 'description', 'skills_required']
        widgets = {
            'description': forms.Textarea(attrs={'rows': 4}),
            'skills_required': forms.Textarea(attrs={'rows': 3}),
        }

class ApplicationForm(forms.ModelForm):
    class Meta:
        model = Application
        fields = ['skills', 'resume', 'cover_letter']
        widgets = {
            'skills': forms.Textarea(attrs={'rows': 3, 'placeholder': 'Enter your skills (comma-separated)'}),
            'cover_letter': forms.Textarea(attrs={'rows': 5, 'placeholder': 'Write your cover letter here...'}),
        }


views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.db.models import Q, Count
from .forms import UserRegisterForm, UserProfileForm, JobForm, ApplicationForm
from .models import UserProfile, Job, Application

def register(request):
    if request.method == 'POST':
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            user = form.save()
            UserProfile.objects.create(
                user=user,
                display_name=form.cleaned_data['display_name'],
                user_type=form.cleaned_data['user_type']
            )
            login(request, user)
            return redirect('portal:profile')
        # else:
        #     messages.error(request, "Registration failed. Please correct the errors below.")
        #     print(form.errors)
    else:
        form = UserRegisterForm()
    return render(request, 'portal/register.html', {'form': form})

def login_view(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(request, username=username, password=password)
        if user:
            login(request, user)
            return redirect("portal:dashboard")
        else:
            messages.error(request, "Invalid credentials")
            return redirect("portal:login")
    return render(request, 'portal/login.html')

@login_required
def profile(request):
    profile, created = UserProfile.objects.get_or_create(user=request.user)
    if request.method == 'POST':
        form = UserProfileForm(request.POST, request.FILES, instance=profile)
        if form.is_valid():
            form.save()
            return redirect('portal:dashboard')
    else:
        form = UserProfileForm(instance=profile)
    return render(request, 'portal/profile.html', {'form': form})


@login_required
def dashboard(request):
    profile, created = UserProfile.objects.get_or_create(user=request.user)
    if profile.user_type == 'recruiter':
        jobs = Job.objects.filter(recruiter=request.user).annotate(applied_count=Count("applications"))
        return render(request, 'portal/recruiter_dashboard.html', {'jobs': jobs})
    else:
        applications = Application.objects.filter(jobseeker=request.user)
        # Skill matching: jobs where skills match user's skills
        user_skills = profile.skills.lower().split(',') if profile.skills else []
        query = Q()
        for skill in user_skills:
            query |= Q(skills_required__icontains=skill.strip())
        matched_jobs = Job.objects.filter(query).exclude(
            id__in=applications.values_list('job_id', flat=True)
        )[:10]  # Limit to 10, exclude already applied jobs
        return render(request, 'portal/jobseeker_dashboard.html', {
            'applications': applications,
            'matched_jobs': matched_jobs
        })


@login_required
def post_job(request):
    if request.method == 'POST':
        form = JobForm(request.POST)
        if form.is_valid():
            job = form.save(commit=False)
            job.recruiter = request.user
            job.save()
            return redirect('portal:dashboard')
    else:
        form = JobForm()
    return render(request, 'portal/post_job.html', {'form': form,  'title': 'Post', 'submit': 'Post'})


def job_list(request):
    jobs = Job.objects.all()
    query = request.GET.get('query', '')
    category = request.GET.get('category', '')

    if query:
        jobs = jobs.filter(
            Q(title__icontains=query) |
            Q(description__icontains=query) |
            Q(skills_required__icontains=query)
        )

    if category and category != "all":
        jobs = jobs.filter(category=category)

    applied_jobs = []
    if hasattr(request.user, 'userprofile') and request.user.userprofile.user_type == 'jobseeker':
        applied_jobs = Application.objects.filter(jobseeker=request.user).values_list('job_id', flat=True)

    return render(request, 'portal/job_list.html', {
        'jobs': jobs,
        'applied_jobs': applied_jobs,
        'query': query,
        'category': category
    })

@login_required
def job_details(request, job_id):
    job = get_object_or_404(Job, id=job_id)
    applied = False
    if request.user.is_authenticated and hasattr(request.user, 'userprofile') and request.user.userprofile.user_type == 'jobseeker':
        applied = Application.objects.filter(jobseeker=request.user, job=job).exists()
    return render(request, 'portal/job_details.html', {'job': job, 'applied': applied})

@login_required
def apply_job(request, job_id):
    job = get_object_or_404(Job, id=job_id)
    profile = UserProfile.objects.get(user=request.user)

    # Check if already applied
    if Application.objects.filter(jobseeker=request.user, job=job).exists():
        messages.info(request, 'You have already applied for this job.')
        return redirect('portal:job_list')

    if request.method == 'POST':
        form = ApplicationForm(request.POST, request.FILES)
        if form.is_valid():
            application = form.save(commit=False)
            application.jobseeker = request.user
            application.job = job
            application.save()
            messages.success(request, 'Application submitted successfully!')
            return redirect('portal:job_list')
    else:
        # Pre-fill form with user's profile data
        initial_data = {
            'skills': profile.skills,
            'resume': profile.resume,
        }
        form = ApplicationForm(initial=initial_data)

    return render(request, 'portal/apply_job.html', {
        'form': form,
        'job': job
    })

@login_required
def manage_applications(request, job_id):
    job = get_object_or_404(Job, id=job_id, recruiter=request.user)
    applications = Application.objects.filter(job=job)

    if request.method == 'POST':
        app_id = request.POST.get('application_id')
        new_status = request.POST.get('status')
        application = get_object_or_404(Application, id=app_id, job=job)
        application.status = new_status
        application.save()
        messages.success(request, f'Application status updated to {new_status}.')
        return redirect('portal:manage_applications', job_id=job_id)

    return render(request, 'portal/manage_applications.html', {
        'job': job,
        'applications': applications
    })

@login_required
def edit_job(request, job_id):
    job = get_object_or_404(Job, id=job_id, recruiter=request.user)
    if request.method == 'POST':
        form = JobForm(request.POST, instance=job)
        if form.is_valid():
            form.save()
            messages.success(request, 'Job updated successfully!')
            return redirect('portal:dashboard')
    else:
        form = JobForm(instance=job)
    return render(request, 'portal/post_job.html', {'form': form, 'job': job, 'title': 'Edit', 'submit': 'Update'})

@login_required
def delete_job(request, job_id):
    job = get_object_or_404(Job, id=job_id, recruiter=request.user)
    if request.method == 'POST':
        job.delete()
        messages.success(request, 'Job deleted successfully!')
        return redirect('portal:dashboard')
    return render(request, 'portal/delete_job.html', {'job': job})

@login_required
def logout_view(request):
    logout(request)
    return redirect('portal:login')



urls.py
from django.urls import path
from . import views

app_name = 'portal'

urlpatterns = [
    path('login/', views.login_view, name='login'),
    path('register/', views.register, name='register'),
    path('logout/', views.logout_view, name='logout'),
   
    path('profile/', views.profile, name='profile'),
    path('dashboard/', views.dashboard, name='dashboard'),
   
    path('post-job/', views.post_job, name='post_job'),
    path('', views.job_list, name='job_list'),
    path('apply/<int:job_id>/', views.apply_job, name='apply_job'),
    path('job/<int:job_id>/', views.job_details, name='job_details'),
    path('manage-applications/<int:job_id>/', views.manage_applications, name='manage_applications'),
    path('edit-job/<int:job_id>/', views.edit_job, name='edit_job'),
    path('delete-job/<int:job_id>/', views.delete_job, name='delete_job'),
]



project/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('portal.urls', namespace='portals')),
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)




base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Job Portal{% endblock %}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        .regilog {
            text-decoration: none;
            color: #0d6efd;
        }
        .regilog:hover {
            color: #08bf8eff;
        }
    </style>
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
        <div class="container">
            <a class="navbar-brand" href="{% url 'portal:job_list' %}">Job Portal</a>
            <div class="navbar-nav ms-auto">
                {% if user.is_authenticated %}
                    <a class="nav-link" href="{% url 'portal:job_list' %}">Jobs</a>
                    <a class="nav-link" href="{% url 'portal:dashboard' %}">Dashboard</a>
                    <a class="nav-link" href="{% url 'portal:profile' %}">Profile</a>
                    <a class="nav-link" href="{% url 'portal:logout' %}">Logout</a>
                {% else %}
                    <a class="nav-link" href="{% url 'portal:login' %}">Login</a>
                    <a class="nav-link" href="{% url 'portal:register' %}">Register</a>
                {% endif %}
            </div>
        </div>
    </nav>
    <div class="container mt-4">
        {% if messages %}
            {% for message in messages %}
                <div class="alert alert-{{ message.tags }}">{{ message }}</div>
            {% endfor %}
        {% endif %}
        {% block content %}{% endblock %}
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>



register.html
{% extends 'portal/base.html' %}

{% block title %}Register{% endblock %}

{% block content %}
<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-6">
            <div class="card shadow">
                <div class="card-header bg-primary text-white">
                    <h3 class="card-title mb-0">Create Account</h3>
                </div>
                <div class="card-body">
                    <form method="post" enctype="multipart/form-data">
                        {% csrf_token %}
                        <div class="mb-3">
                            <label for="username" class="form-label">Username</label>
                            <input type="text" class="form-control" id="username" name="username" required>
                        </div>
                        <div class="mb-3">
                            <label for="display_name" class="form-label">Display Name</label>
                            <input type="text" class="form-control" id="display_name" name="display_name" required>
                        </div>
                        <div class="mb-3">
                            <label for="email" class="form-label">Email</label>
                            <input type="email" class="form-control" id="email" name="email" required>
                        </div>
                        <div class="mb-3">
                            <label for="user_type" class="form-label">User Type</label>
                            <select class="form-control" id="user_type" name="user_type" required>
                                <option value="">Select User Type</option>
                                <option value="recruiter">Recruiter</option>
                                <option value="jobseeker">Jobseeker</option>
                            </select>
                        </div>
                        <div class="mb-3">
                            <label for="password1" class="form-label">Password</label>
                            <input type="password" class="form-control" id="password1" name="password1" required>
                        </div>
                        <div class="mb-3">
                            <label for="password2" class="form-label">Confirm Password</label>
                            <input type="password" class="form-control" id="password2" name="password2" required>
                        </div>
                        <button type="submit" class="btn btn-primary w-100">Register</button>
                    </form>
                    <div class="text-center mt-3">
                        <p>Already have an account? <a class="regilog" href="{% url 'portal:login' %}"><b>Login here</b></a></p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}



login.html
{% extends 'portal/base.html' %}

{% block title %}Login{% endblock %}

{% block content %}
<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-6">
            <div class="card shadow">
                <div class="card-header bg-primary text-white">
                    <h3 class="card-title mb-0">Login</h3>
                </div>
                <div class="card-body">
                    <form method="post">
                        {% csrf_token %}
                        <div class="mb-3">
                            <label for="username" class="form-label">Username</label>
                            <input type="text" class="form-control" id="username" name="username" required>
                        </div>
                        <div class="mb-3">
                            <label for="password" class="form-label">Password</label>
                            <input type="password" class="form-control" id="password" name="password" required>
                        </div>
                        <button type="submit" class="btn btn-primary w-100">Login</button>
                    </form>
                    <div class="text-center mt-3">
                        <p>Don't have an account? <a class="regilog" href="{% url 'portal:register' %}"><b>Register here</b></a></p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}



profile.html
{% extends 'portal/base.html' %}

{% block title %}Profile{% endblock %}

{% block content %}
<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card shadow">
                <div class="card-header bg-primary text-white">
                    <h3 class="card-title mb-0">Complete Your Profile</h3>
                </div>
                <div class="card-body">
                    <form method="post" enctype="multipart/form-data">
                        {% csrf_token %}
                        <div class="mb-3">
                            <label for="display_name" class="form-label">Display Name</label>
                            <input type="text" class="form-control" id="display_name" name="display_name" value="{{ form.display_name.value|default:'' }}" required>
                        </div>
                        <div class="mb-3">
                            <label for="user_type" class="form-label">User Type</label>
                            <select class="form-control" id="user_type" name="user_type" required>
                                <option value="">Select User Type</option>
                                <option value="recruiter" {% if form.user_type.value == 'recruiter' %}selected{% endif %}>Recruiter</option>
                                <option value="jobseeker" {% if form.user_type.value == 'jobseeker' %}selected{% endif %}>Jobseeker</option>
                            </select>
                        </div>
                        {% if form.user_type.value == 'recruiter' %}
                            <div class="mb-3">
                                <label for="company_name" class="form-label">Company Name</label>
                                <input type="text" class="form-control" id="company_name" name="company_name" value="{{ form.company_name.value|default:'' }}">
                            </div>
                            <div class="mb-3">
                                <label for="company_description" class="form-label">Company Description</label>
                                <textarea class="form-control" id="company_description" name="company_description" rows="4">{{ form.company_description.value|default:'' }}</textarea>
                            </div>
                        {% else %}
                            <div class="mb-3">
                                <label for="skills" class="form-label">Skills (comma-separated)</label>
                                <input type="text" class="form-control" id="skills" name="skills" value="{{ form.skills.value|default:'' }}">
                            </div>
                            <div class="mb-3">
                                <label for="resume" class="form-label">Resume</label>
                                <input type="file" class="form-control" id="resume" name="resume">
                            </div>
                        {% endif %}
                        <button type="submit" class="btn btn-primary">Save Profile</button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}



recruiter-dashboard.html
{% extends 'portal/base.html' %}

{% block title %}Recruiter Dashboard{% endblock %}

{% block content %}
<div class="container mt-5">
    <h2>Welcome, {{ user.username }}!</h2>
    <p>You are logged in as a Recruiter.</p>

    <div class="mb-4">
        <a href="{% url 'portal:post_job' %}" class="btn btn-primary">Post a New Job</a>
    </div>

    <h3>Your Jobs:</h3>
    {% if jobs %}
        <div class="row">
            {% for job in jobs %}
                <div class="col-md-4 mb-4">
                    <div class="card shadow">
                        <div class="card-body">
                            <h5 class="card-title"><a href="{% url 'portal:job_details' job.id %}" class="text-decoration-none">{{ job.title }}</a></h5>
                            <p class="card-text"><strong>Skills:</strong> {{ job.skills_required }}</p>
                            <p class="card-text"><strong>Applied: {{ job.applied_count }} </strong></p>
                            <small class="text-muted">Posted on: {{ job.created_at|date:"M d, Y" }}</small>
                            <div class="mt-3">
                                <a href="{% url 'portal:manage_applications' job.id %}" class="btn btn-sm btn-info">View Applications</a>
                                <a href="{% url 'portal:edit_job' job.id %}" class="btn btn-sm btn-warning">Edit Job</a>
                                <a href="{% url 'portal:delete_job' job.id %}" class="btn btn-sm btn-danger">Delete Job</a>
                            </div>
                        </div>
                    </div>
                </div>
            {% endfor %}
        </div>
    {% else %}
        <p>You haven't posted any jobs yet.</p>
    {% endif %}
</div>
{% endblock %}


job-seeker-dashboard.html
{% extends 'portal/base.html' %}

{% block title %}Jobseeker Dashboard{% endblock %}

{% block content %}
<div class="container mt-5">
    <div class="row">
        <div class="col-12">
            <h2 class="mb-4">Welcome, {{ user.username }}!</h2>
            <p>You are logged in as a Jobseeker.</p>
        </div>
    </div>

    <div class="row">
        <div class="col-md-6">
            <div class="card shadow mb-4">
                <div class="card-header bg-success text-white">
                    <h5 class="card-title mb-0">Your Applications</h5>
                </div>
                <div class="card-body">
                    {% if applications %}
                        <div class="list-group list-group-flush">
                            {% for application in applications %}
                                <div class="list-group-item">
                                    <h6 class="mb-1"><a href="{% url 'portal:job_details' application.job.id %}">{{ application.job.title }}</a></h6>
                                    <p class="mb-1">{{ application.job.recruiter.username }}</p>
                                    <small class="text-muted">Applied on {{ application.applied_at|date:"M d, Y" }}</small>
                                    <span class="badge bg-{% if application.status == 'pending' %}warning{% elif application.status == 'accepted' %}success{% else %}danger{% endif %} ms-2">{{ application.status|title }}</span>
                                </div>
                            {% endfor %}
                        </div>
                    {% else %}
                        <p class="text-muted">You haven't applied to any jobs yet.</p>
                    {% endif %}
                </div>
            </div>
        </div>

        <div class="col-md-6">
            <div class="card shadow mb-4">
                <div class="card-header bg-info text-white">
                    <h5 class="card-title mb-0">Matching Jobs with Skills</h5>
                </div>
                <div class="card-body">
                    {% if matched_jobs %}
                        <div class="list-group list-group-flush">
                            {% for job in matched_jobs %}
                                <div class="list-group-item">
                                    <h6 class="mb-1"><a href="{% url 'portal:job_details' job.id %}">{{ job.title }}</a></h6>
                                    <p class="mb-1">{{ job.recruiter.username }}</p>
                                    <small class="text-muted">{{ job.category|title }}</small>
                                    <a href="{% url 'portal:apply_job' job.id %}" class="btn btn-sm btn-primary mt-2">Apply Now</a>
                                </div>
                            {% endfor %}
                        </div>
                    {% else %}
                        <p class="text-muted">No recommended jobs available. Update your skills in your profile.</p>
                    {% endif %}
                </div>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-12">
            <div class="card shadow">
                <div class="card-header bg-primary text-white">
                    <h5 class="card-title mb-0">All Available Jobs</h5>
                </div>
                <div class="card-body">
                    <a href="{% url 'portal:job_list' %}" class="btn btn-primary">Browse All Jobs</a>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}



post-job.html
{% extends 'portal/base.html' %}

{% block title %}Post Job{% endblock %}

{% block content %}
<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card shadow">
                <div class="card-header bg-primary text-white text-center">
                    <h3 class="card-title mb-0">{{title}} a New Job</h3>
                </div>
                <div class="card-body">
                    <form method="post">
                        {% csrf_token %}
                        <div class="mb-3">
                            <label for="title" class="form-label">Job Title</label>
                            <input type="text" class="form-control" id="title" name="title" value="{{ form.title.value|default:'' }}" required>
                        </div>
                        <div class="mb-3">
                            <label for="number_of_openings" class="form-label">Number of Openings</label>
                            <input type="number" class="form-control" id="number_of_openings" name="number_of_openings" value="{{ form.number_of_openings.value|default:'' }}" required>
                        </div>
                        <div class="mb-3">
                            <label for="category" class="form-label">Category</label>
                            <select class="form-control" id="category" name="category" required>
                                <option value="">Select Category</option>
                                <option value="tech" {% if form.category.value == 'tech' %}selected{% endif %}>Technology</option>
                                <option value="finance" {% if form.category.value == 'finance' %}selected{% endif %}>Finance</option>
                                <option value="healthcare" {% if form.category.value == 'healthcare' %}selected{% endif %}>Healthcare</option>
                                <option value="education" {% if form.category.value == 'education' %}selected{% endif %}>Education</option>
                                <option value="other" {% if form.category.value == 'other' %}selected{% endif %}>Other</option>
                            </select>
                        </div>
                        <div class="mb-3">
                            <label for="description" class="form-label">Job Description</label>
                            <textarea class="form-control" id="description" name="description" rows="4" required>{{ form.description.value|default:'' }}</textarea>
                        </div>
                        <div class="mb-3">
                            <label for="skills_required" class="form-label">Skills Required (comma-separated)</label>
                            <input type="text" class="form-control" id="skills_required" name="skills_required" value="{{ form.skills_required.value|default:'' }}" required>
                        </div>
                        <div class="text-center">
                            <button type="submit" class="btn btn-primary">{{submit}} Job</button>
                            <a href="{% url 'portal:dashboard' %}" class="btn btn-secondary">Cancel</a>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}



job-lists.html
{% extends 'portal/base.html' %}

{% block title %}Available Jobs{% endblock %}

{% block content %}
<div class="container mt-5">
    <div class="row">
        <div class="col-12">
            <h2 class="mb-4">Available Jobs</h2>
            <form method="get" class="mb-4">
                <div class="row g-3">
                    <div class="col-md-6">
                        <input type="text" name="query" class="form-control" placeholder="Search jobs..." value="{{ query }}">
                    </div>
                    <div class="col-md-4">
                        <select name="category" class="form-select">
                            <option value="all" {% if not category or category == 'all' %}selected{% endif %}>All Categories</option>
                            <option value="tech" {% if category == 'tech' %}selected{% endif %}>Technology</option>
                            <option value="finance" {% if category == 'finance' %}selected{% endif %}>Finance</option>
                            <option value="healthcare" {% if category == 'healthcare' %}selected{% endif %}>Healthcare</option>
                            <option value="education" {% if category == 'education' %}selected{% endif %}>Education</option>
                            <option value="other" {% if category == 'other' %}selected{% endif %}>Other</option>
                        </select>
                    </div>
                    <div class="col-md-2">
                        <button type="submit" class="btn btn-primary w-100">Search</button>
                    </div>
                </div>
            </form>
        </div>
    </div>

    <div class="row">
        {% for job in jobs %}
            <div class="col-md-4 mb-4">
                <div class="card shadow h-100">
                    <div class="card-header bg-light">
                        <h5 class="card-title mb-0"><a href="{% url 'portal:job_details' job.id %}" class="text-decoration-none">{{ job.title }}</a></h5>
                        <small class="text-muted">{{ job.recruiter.username }}</small>
                    </div>
                    <div class="card-body">
                        <div class="mb-2">
                            <span class="badge bg-secondary">{{ job.category|title }}</span>
                            <span class="badge bg-info">{{ job.number_of_openings }} openings</span>
                        </div>
                        <div class="mb-3">
                            <strong>Skills Required:</strong>
                            <p class="mb-0">{{ job.skills_required }}</p>
                        </div>
                        <small class="text-muted">Posted on {{ job.created_at|date:"M d, Y" }}</small>
                    </div>
                    <div class="card-footer">
                        {% if user.is_authenticated and user.userprofile.user_type == 'jobseeker' %}
                            {% if job.id not in applied_jobs %}
                                <a href="{% url 'portal:apply_job' job.id %}" class="btn btn-primary">Apply Now</a>
                            {% else %}
                                <button class="btn btn-success" disabled>Already Applied</button>
                            {% endif %}
                        {% endif %}
                    </div>
                </div>
            </div>
        {% empty %}
            <div class="col-12">
                <div class="alert alert-info">
                    <h4>No jobs available</h4>
                    <p>Check back later for new job opportunities.</p>
                </div>
            </div>
        {% endfor %}
    </div>
</div>
{% endblock %}


job-detail.html
{% extends 'portal/base.html' %}

{% block title %}{{ job.title }} - Job Details{% endblock %}

{% block content %}
<div class="container mt-5">
    <div class="mb-3">
        <a href="{% url 'portal:job_list' %}" class="btn btn-secondary">&larr; Back to Jobs</a>
    </div>
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card shadow">
                <div class="card-header bg-primary text-white">
                    <h3 class="card-title mb-0">{{ job.title }}</h3>
                    <small class="text-muted">Posted by: {{ job.recruiter.username }}</small>
                </div>
                <div class="card-body">
                    <div class="mb-4">
                        <h5>Job Details:</h5>
                        <p><strong>Category:</strong> {{ job.get_category_display }}</p>
                        <p><strong>Openings:</strong> {{ job.number_of_openings }}</p>
                        <p><strong>Description:</strong> {{ job.description }}</p>
                        <p><strong>Required Skills:</strong> {{ job.skills_required }}</p>
                        <p><strong>Posted on:</strong> {{ job.created_at|date:"d M Y" }}</p>
                    </div>

                    {% if user.is_authenticated and user.userprofile.user_type == 'jobseeker' %}
                        <div class="text-center">
                            {% if applied %}
                                <button class="btn btn-success btn-lg" disabled>You have already applied for this job</button>
                            {% else %}
                                <a href="{% url 'portal:apply_job' job.id %}" class="btn btn-primary btn-lg">Apply Now</a>
                            {% endif %}
                        </div>
                    {% endif %}
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}



apply-job.html
{% extends 'portal/base.html' %}

{% block content %}
<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-6">
            <div class="card shadow">
                <div class="card-header bg-primary text-white">
                    <h3 class="card-title mb-0">Apply for: {{ job.title }}</h3>
                    <small class="text-muted">Posted by: {{ job.recruiter.username }}</small>
                </div>
                <div class="card-body">
                    <div class="mb-4">
                        <h5>Job Details:</h5>
                        <p><strong>Category:</strong> {{ job.get_category_display }}</p>
                        <p><strong>Openings:</strong> {{ job.number_of_openings }}</p>
                        <p><strong>Description:</strong> {{ job.description }}</p>
                        <p><strong>Required Skills:</strong> {{ job.skills_required }}</p>
                    </div>

                    <h5>Your Application:</h5>
                    <form method="post" enctype="multipart/form-data">
                        {% csrf_token %}
                        <div class="mb-3">
                            <label for="skills" class="form-label">Your Skills</label>
                            <textarea class="form-control" id="skills" name="skills" rows="3" placeholder="Enter your skills (comma-separated)" required>{{ form.skills.value|default:'' }}</textarea>
                            {% if form.skills.errors %}
                                <div class="text-danger">{{ form.skills.errors }}</div>
                            {% endif %}
                        </div>

                        <div class="mb-3">
                            <label for="resume" class="form-label">Resume</label>
                            <input type="file" class="form-control" id="resume" name="resume" accept=".pdf,.doc,.docx">
                            {% if form.resume.errors %}
                                <div class="text-danger">{{ form.resume.errors }}</div>
                            {% endif %}
                            <div class="form-text">Upload your resume (PDF, DOC, DOCX)</div>
                        </div>

                        <div class="mb-3">
                            <label for="cover_letter" class="form-label">Cover Letter</label>
                            <textarea class="form-control" id="cover_letter" name="cover_letter" rows="5" placeholder="Write your cover letter here...">{{ form.cover_letter.value|default:'' }}</textarea>
                            {% if form.cover_letter.errors %}
                                <div class="text-danger">{{ form.cover_letter.errors }}</div>
                            {% endif %}
                        </div>

                        <button type="submit" class="btn btn-primary w-100">Submit Application</button>
                    </form>
                    <div class="text-center mt-3">
                        <a href="{% url 'portal:job_list' %}" class="btn btn-secondary">Cancel</a>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}



manage-application.html
{% extends 'portal/base.html' %}

{% block title %}Manage Applications for {{ job.title }}{% endblock %}

{% block content %}
<div class="container mt-5">
    <h2>Applications for: {{ job.title }}</h2>
    <p>Posted by: {{ job.recruiter.username }}</p>

    {% if applications %}
        <div class="row">
            {% for application in applications %}
                <div class="col-md-6 mb-4">
                    <div class="card shadow">
                        <div class="card-body">
                            <h5 class="card-title">{{ application.jobseeker.username }}</h5>
                            <p class="card-text"><strong>Skills:</strong> {{ application.skills }}</p>
                            <p class="card-text"><strong>Cover Letter:</strong> {{ application.cover_letter|truncatechars:100 }}</p>
                            <p class="card-text"><strong>Status:</strong> <span class="badge bg-{% if application.status == 'pending' %}warning{% elif application.status == 'shortlisted' %}info{% elif application.status == 'selected' %}success{% else %}danger{% endif %}">{{ application.status|title }}</span></p>
                            {% comment %} <p class="card-text"><strong>Applied:</strong> {{ applications|length }}</p> {% endcomment %}
                            <p class="card-text"><small class="text-muted">Applied on: {{ application.applied_at|date:"M d, Y" }}</small></p>
                            {% if application.resume %}
                                <a href="{{ application.resume.url }}" class="btn btn-sm btn-outline-primary" target="_blank">View Resume</a>
                            {% endif %}
                            <form method="post" class="d-inline">
                                {% csrf_token %}
                                <input type="hidden" name="application_id" value="{{ application.id }}">
                                <select name="status" class="form-select form-select-sm d-inline w-auto">
                                    <option value="pending" {% if application.status == 'pending' %}selected{% endif %}>Pending</option>
                                    <option value="shortlisted" {% if application.status == 'shortlisted' %}selected{% endif %}>Shortlisted</option>
                                    <option value="selected" {% if application.status == 'selected' %}selected{% endif %}>Selected</option>
                                    <option value="rejected" {% if application.status == 'rejected' %}selected{% endif %}>Rejected</option>
                                </select>
                                <button type="submit" class="btn btn-sm btn-primary">Update</button>
                            </form>
                        </div>
                    </div>
                </div>
            {% endfor %}
        </div>
    {% else %}
        <p>No applications yet.</p>
    {% endif %}

    <div class="mt-4">
        <a href="{% url 'portal:dashboard' %}" class="btn btn-secondary">Back to Dashboard</a>
    </div>
</div>
{% endblock %}



delete-job.html
{% extends 'portal/base.html' %}

{% block title %}Delete Job{% endblock %}

{% block content %}
<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-6">
            <div class="card shadow">
                <div class="card-header bg-danger text-white">
                    <h3 class="card-title mb-0">Delete Job</h3>
                </div>
                <div class="card-body">
                    <p>Are you sure you want to delete the job "<strong>{{ job.title }}</strong>"?</p>
                    <p class="text-muted">This action cannot be undone.</p>
                    <form method="post">
                        {% csrf_token %}
                        <button type="submit" class="btn btn-danger">Yes, Delete Job</button>
                        <a href="{% url 'portal:dashboard' %}" class="btn btn-secondary">Cancel</a>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}



settings.py
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-jvf&_zpgdrh&r9uah0xo(97yy&fez$u+c1#m7uxvfj17s#!-uq'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'portal',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'JobPortal.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'JobPortal.wsgi.application'


# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_URL = "portal:login"

No comments:

Post a Comment