Build a Job Search Portal with Django — Part 3
By: Kumar Shubham
Tags: Programming, Bycetutorial, Coding, Django, Python

views.py

Now we will deal with the views.py file which contains all the views which are related to the candidates part of our app.

It is the longest part of the backend section of the Candidates app since many things are working in this part and almost all the logic of the app is being written here. So, we will try to break these in small parts, taking a single function at a time and discussing it.

First up, we need to import various things, all of which has separate functions. We will discuss them when they are being used.

So, let’s start:-

home

This is the simplest function, which is just there to serve the homepage. We do not have much going in it.

def home(request):

context = {

'home_page': "active",

}

return render(request, 'candidates/home.html', context)

job_search_list

This is used to search for jobs by the candidates. It takes in the job query and the location of the job the candidate is looking for.

We then build a list which consists of the jobs which the candidate will see when he clicks on the search button.

If the user does not give any input, we display all the jobs. Otherwise, we match the query to job type, title, skill and company names and combine all results we get in a single list.

We then do the same for location data. If it is provided, then we filter out jobs from that location, otherwise, we just take in all the jobs.

Next, we will take the union of the query list and location list to make the final list. We also apply a paginator to restrict each page of the search result in 20 job posts.

We then pass all this data to the template which would then display the job search results.

def job_search_list(request):

query = request.GET.get('p')

loc = request.GET.get('q')

object_list = []

if(query == None):

object_list = Job.objects.all()

else:

title_list = Job.objects.filter(

title__icontains=query).order_by('-date_posted')

skill_list = Job.objects.filter(

skills_req__icontains=query).order_by('-date_posted')

company_list = Job.objects.filter(

company__icontains=query).order_by('-date_posted')

job_type_list = Job.objects.filter(

job_type__icontains=query).order_by('-date_posted')

for i in title_list:

object_list.append(i)

for i in skill_list:

if i not in object_list:

object_list.append(i)

for i in company_list:

if i not in object_list:

object_list.append(i)

for i in job_type_list:

if i not in object_list:

object_list.append(i)

if(loc == None):

locat = Job.objects.all()

else:

locat = Job.objects.filter(

location__icontains=loc).order_by('-date_posted')

final_list = []

for i in object_list:

if i in locat:

final_list.append(i)

paginator = Paginator(final_list, 20)

page_number = request.GET.get('page')

page_obj = paginator.get_page(page_number)

context = {

'jobs': page_obj,

'query': query,

}

return render(request, 'candidates/job_search_list.html', context)

job_detail

This is the function which will display the job post in detail. It has two variables — apply_button (checks whether the user has applied for the job or not) and save_button (checks whether the user has saved the job or not) which stores the data related to the job the candidate is looking at.

We then make a list which consists of the jobs which are similar to this one, to recommend to the user. This is produced by considering the job type, title and company of the job the user is looking at.

def job_detail(request, slug):

job = get_object_or_404(Job, slug=slug)

apply_button = 0

save_button = 0

profile = Profile.objects.filter(user=request.user).first()

if AppliedJobs.objects.filter(user=request.user).filter(job=job).exists():

apply_button = 1

if SavedJobs.objects.filter(user=request.user).filter(job=job).exists():

save_button = 1

relevant_jobs = []

jobs1 = Job.objects.filter(

company__icontains=job.company).order_by('-date_posted')

jobs2 = Job.objects.filter(

job_type__icontains=job.job_type).order_by('-date_posted')

jobs3 = Job.objects.filter(

title__icontains=job.title).order_by('-date_posted')

for i in jobs1:

if len(relevant_jobs) > 5:

break

if i not in relevant_jobs and i != job:

relevant_jobs.append(i)

for i in jobs2:

if len(relevant_jobs) > 5:

break

if i not in relevant_jobs and i != job:

relevant_jobs.append(i)

for i in jobs3:

if len(relevant_jobs) > 5:

break

if i not in relevant_jobs and i != job:

relevant_jobs.append(i)



return render(request, 'candidates/job_detail.html', {'job': job, 'profile': profile, 'apply_button': apply_button, 'save_button': save_button, 'relevant_jobs': relevant_jobs, 'candidate_navbar': 1})

saved_jobs

This is a very simple view function which just takes the saved jobs by the user and orders them in order of date posted. It then renders it using a template.

Access to this function’s rendered template, the user needs to have logged in which is checked by the login_required decorator.

@login_required

def saved_jobs(request):

jobs = SavedJobs.objects.filter(

user=request.user).order_by('-date_posted')

return render(request, 'candidates/saved_jobs.html', {'jobs': jobs, 'candidate_navbar': 1})

applied_jobs

This function is used to display all the jobs the user has applied to. It also requires the user to have logged in to access this page.

It also has a status list, which contains the status of all the applications of the user, whether they had been selected, rejected or is pending.

@login_required

def applied_jobs(request):

jobs = AppliedJobs.objects.filter(

user=request.user).order_by('-date_posted')

statuses = []

for job in jobs:

if Selected.objects.filter(job=job.job).filter(applicant=request.user).exists():

statuses.append(0)

elif Applicants.objects.filter(job=job.job).filter(applicant=request.user).exists():

statuses.append(1)

else:

statuses.append(2)

zipped = zip(jobs, statuses)

return render(request, 'candidates/applied_jobs.html', {'zipped': zipped, 'candidate_navbar': 1})

intelligent_search

This function is used to show jobs to a candidate which suits the skill set of the user and matches the job type the user is looking for.

This filters out jobs first based on what candidate is looking for and then processes all these jobs and get the skills they require. We also get the skills a candidate possesses in a list. We then keep matching these two lists and if we get more than half of the skills required by the job post matched by candidate’s skills then we recommend that job post on that page.

Also, we order these job posts by the skill match ratio. Higher the skill match percentage, higher the position it will occupy in our list. We then zip the job list, skills of candidates and common skills count and pass it on to the template. These will help us to write the number of skills matched in the job description on that page.

@login_required

def intelligent_search(request):

relevant_jobs = []

common = []

job_skills = []

user = request.user

profile = Profile.objects.filter(user=user).first()

my_skill_query = Skill.objects.filter(user=user)

my_skills = []

for i in my_skill_query:

my_skills.append(i.skill.lower())

if profile:

jobs = Job.objects.filter(

job_type=profile.looking_for).order_by('-date_posted')

else:

jobs = Job.objects.all()

for job in jobs:

skills = []

sk = str(job.skills_req).split(",")

for i in sk:

skills.append(i.strip().lower())

common_skills = list(set(my_skills) & set(skills))

if (len(common_skills) != 0 and len(common_skills) >= len(skills)//2):

relevant_jobs.append(job)

common.append(len(common_skills))

job_skills.append(len(skills))

objects = zip(relevant_jobs, common, job_skills)

objects = sorted(objects, key=lambda t: t[1]/t[2], reverse=True)

objects = objects[:100]

context = {

'intel_page': "active",

'jobs': objects,

'counter': len(relevant_jobs),

}

return render(request, 'candidates/intelligent_search.html', context)

my_profile

This function holds all the data for the candidate profile. It displays the user profile and also the skills possessed by the user. It also has the ability to add new skills by using NewSkillForm. This also requires a user to have logged in first.

@login_required

def my_profile(request):

you = request.user

profile = Profile.objects.filter(user=you).first()

user_skills = Skill.objects.filter(user=you)

if request.method == 'POST':

form = NewSkillForm(request.POST)

if form.is_valid():

data = form.save(commit=False)

data.user = you

data.save()

return redirect('my-profile')

else:

form = NewSkillForm()

context = {

'u': you,

'profile': profile,

'skills': user_skills,

'form': form,

'profile_page': "active",

}

return render(request, 'candidates/profile.html', context)

edit_profile

This function allows candidates to edit their profiles. It makes use of the ProfileUpdateForm to do so. This also requires a user to have logged in first. It allows only the owner of the profile to edit it.

@login_required

def edit_profile(request):

you = request.user

profile = Profile.objects.filter(user=you).first()

if request.method == 'POST':

form = ProfileUpdateForm(request.POST, request.FILES, instance=profile)

if form.is_valid():

data = form.save(commit=False)

data.user = you

data.save()

return redirect('my-profile')

else:

form = ProfileUpdateForm(instance=profile)

context = {

'form': form,

}

return render(request, 'candidates/edit_profile.html', context)

profile_view

This view function is similar to the my_profile function but with a small difference. Unlike the my_profile, which was for viewing your own profile, profile_view is for recruiters to view your profile. So, we do not allow skills editing in this view. It also requires the user to have logged in.

@login_required

def profile_view(request, slug):

p = Profile.objects.filter(slug=slug).first()

you = p.user

user_skills = Skill.objects.filter(user=you)

context = {

'u': you,

'profile': p,

'skills': user_skills,

}

return render(request, 'candidates/profile.html', context)





def candidate_details(request):

return render(request, 'candidates/details.html')

delete_skill

This view function is used to delete the skills of the user. It allows the user to select the skills they want to delete by checkboxes and then click on the delete button.

Obviously, the user needs to be logged in to perform this step. We also use a csrf_exempt in this function. To know more about csrf, view this official documentation.

@login_required

@csrf_exempt

def delete_skill(request, pk=None):

if request.method == 'POST':

id_list = request.POST.getlist('choices')

for skill_id in id_list:

Skill.objects.get(id=skill_id).delete()

return redirect('my-profile')

save_job

This is used to save any job which the candidate likes and want to store it for further viewing. It adds the job in the SavedJobs model class. User needs to be logged in to do so.

@login_required

def save_job(request, slug):

user = request.user

job = get_object_or_404(Job, slug=slug)

saved, created = SavedJobs.objects.get_or_create(job=job, user=user)

return HttpResponseRedirect('/job/{}'.format(job.slug))

apply_job

This is used to apply to any job which the candidate likes. It adds the job in the AppliedJobs model class. User needs to be logged in to do so. It also adds this to the Applicants model class of the Recruiter App.

@login_required

def apply_job(request, slug):

user = request.user

job = get_object_or_404(Job, slug=slug)

applied, created = AppliedJobs.objects.get_or_create(job=job, user=user)

applicant, creation = Applicants.objects.get_or_create(

job=job, applicant=user)

return HttpResponseRedirect('/job/{}'.format(job.slug))

delete_job

This is used to delete a job from the saved jobs list. It requires the user to ave logged in.

@login_required

def remove_job(request, slug):

user = request.user

job = get_object_or_404(Job, slug=slug)

saved_job = SavedJobs.objects.filter(job=job, user=user).first()

saved_job.delete()

return HttpResponseRedirect('/job/{}'.format(job.slug))

So, that sums up the views.py file of the Candidates App. Here’s a look at the complete code:-