From 34b77e8a4ebd2e32bb1d81601f62079bbbe83f60 Mon Sep 17 00:00:00 2001 From: Aatish Neupane Date: Sat, 20 Oct 2018 12:44:28 +0545 Subject: [PATCH] use formsets for screening questions, create views for applying to jobs --- jobs/admin.py | 4 +- jobs/forms.py | 33 +++++++++++++- jobs/migrations/0003_auto_20181020_0622.py | 23 ++++++++++ jobs/models.py | 1 + jobs/templates/jobs/application_form.html | 13 ++++++ jobs/templates/jobs/job_detail.html | 2 +- jobs/urls.py | 12 ++--- jobs/views.py | 53 +++++++++++++++++++++- 8 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 jobs/migrations/0003_auto_20181020_0622.py create mode 100644 jobs/templates/jobs/application_form.html diff --git a/jobs/admin.py b/jobs/admin.py index 617a6d1..862418b 100644 --- a/jobs/admin.py +++ b/jobs/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from .models import Job, Application, Tag, Question +from .models import Job, Application, Tag, Question, Answer -admin.site.register([Job, Application, Tag, Question]) \ No newline at end of file +admin.site.register([Job, Application, Tag, Question, Answer]) diff --git a/jobs/forms.py b/jobs/forms.py index 86550e1..66bb207 100644 --- a/jobs/forms.py +++ b/jobs/forms.py @@ -2,7 +2,7 @@ from django import forms from django.forms import inlineformset_factory from dal.autocomplete import ModelSelect2Multiple -from .models import Job, Question +from .models import Job, Question, Application, Answer class JobForm(forms.ModelForm): @@ -23,8 +23,37 @@ class QuestionForm(forms.ModelForm): } +class ApplicationForm(forms.ModelForm): + class Meta: + model = Application + exclude = ('job', 'applicant') + + +class AnswerForm(forms.ModelForm): + class Meta: + model = Answer + fields = '__all__' + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['text'].required = True + self.fields['question'].disabled = True + + @classmethod + def inlineformset_factory(cls, extra=3): + return inlineformset_factory( + Application, + Answer, + form=cls, + min_num=extra, + max_num=extra, + validate_min=True, + can_delete=False, + can_order=False) + + QuestionFormSet = inlineformset_factory( Job, Question, form=QuestionForm, can_delete=False, can_order=False) QuestionUpdateFormSet = inlineformset_factory( - Job, Question, form=QuestionForm, can_delete=True, can_order=False) \ No newline at end of file + Job, Question, form=QuestionForm, can_delete=True, can_order=False) diff --git a/jobs/migrations/0003_auto_20181020_0622.py b/jobs/migrations/0003_auto_20181020_0622.py new file mode 100644 index 0000000..bdc34b8 --- /dev/null +++ b/jobs/migrations/0003_auto_20181020_0622.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1.2 on 2018-10-20 06:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jobs', '0002_auto_20181017_1104'), + ] + + operations = [ + migrations.AddField( + model_name='application', + name='cover_letter', + field=models.TextField(blank=True), + ), + migrations.AlterField( + model_name='question', + name='name', + field=models.TextField(verbose_name='Question Title'), + ), + ] diff --git a/jobs/models.py b/jobs/models.py index 952f48a..7c16664 100644 --- a/jobs/models.py +++ b/jobs/models.py @@ -70,6 +70,7 @@ class Application(models.Model): User, related_name="applications", on_delete=models.CASCADE) job = models.ForeignKey( Job, related_name="applications", on_delete=models.CASCADE) + cover_letter = models.TextField(blank=True) created = models.DateTimeField(auto_now_add=True) diff --git a/jobs/templates/jobs/application_form.html b/jobs/templates/jobs/application_form.html new file mode 100644 index 0000000..9bf681f --- /dev/null +++ b/jobs/templates/jobs/application_form.html @@ -0,0 +1,13 @@ +{% extends 'base.html' %} {% block title %}Apply to job{% endblock %} {% load crispy_forms_tags %} {% block body_content %} +
+
+
+ {% csrf_token %} {{ form |crispy }} +

Screening Questions

+ {{ answer_form |crispy }} + +
+ {{form.media}} +
+
+{% endblock %} \ No newline at end of file diff --git a/jobs/templates/jobs/job_detail.html b/jobs/templates/jobs/job_detail.html index dc8d05f..cdb0166 100644 --- a/jobs/templates/jobs/job_detail.html +++ b/jobs/templates/jobs/job_detail.html @@ -22,7 +22,7 @@ - {{question.name}}
{% endfor %}
- Apply + Apply diff --git a/jobs/urls.py b/jobs/urls.py index d275e68..7d1e12a 100644 --- a/jobs/urls.py +++ b/jobs/urls.py @@ -1,17 +1,17 @@ from django.urls import path -from . import views +from .views import Index, JobCreate, JobList, JobDetail, ApplicationCreate from . import autocomplete as autocomplete_views app_name = 'jobs' urlpatterns = [ - path('', views.Index.as_view(), name='index'), - path('jobs/create/', views.JobCreate.as_view(), name='create'), - path('jobs/', views.JobList.as_view(), name='list'), - path('jobs//detail/', views.JobDetail.as_view(), name='job_detail'), + path('', Index.as_view(), name='index'), + path('jobs/create/', JobCreate.as_view(), name='create'), + path('jobs/', JobList.as_view(), name='list'), + path('jobs//detail/', JobDetail.as_view(), name='job_detail'), + path('jobs//apply/', ApplicationCreate.as_view(), name='job_apply'), ] - # autocomplete endpoints urlpatterns += [ path( diff --git a/jobs/views.py b/jobs/views.py index 4a8dd47..95e247f 100644 --- a/jobs/views.py +++ b/jobs/views.py @@ -4,8 +4,8 @@ from django.views.generic import ( TemplateView, ListView, CreateView, DetailView ) -from .models import Job -from .forms import JobForm, QuestionFormSet +from .models import Job, Application, Question +from .forms import JobForm, QuestionFormSet, ApplicationForm, AnswerForm class Index(TemplateView): @@ -59,3 +59,52 @@ class JobCreate(CreateView): self.get_context_data( form=form, question_form=question_form)) + + +class ApplicationCreate(CreateView): + # TODO: restrict users from re-application + model = Application + form_class = ApplicationForm + success_url = reverse_lazy("jobs:list") + + def get_question_queryset(self): + return Question.objects.filter(job_id=self.kwargs['job_pk']).order_by('id') + + def get_answer_formset(self, *args): + questions = self.get_question_queryset() + return AnswerForm.inlineformset_factory( + extra=questions.count())(initial=[{ + 'question': q.id + } for q in questions], *args) + + def get(self, request, *args, **kwargs): + self.object = None + form = self.get_form(self.get_form_class()) + answer_form = self.get_answer_formset() + return self.render_to_response( + self.get_context_data(form=form, answer_form=answer_form)) + + def post(self, request, *args, **kwargs): + self.object = None + form_class = self.get_form_class() + form = self.get_form(form_class) + answer_form = self.get_answer_formset(self.request.POST) + if (form.is_valid() and answer_form.is_valid()): + return self.form_valid(form, answer_form) + else: + return self.form_invalid(form, answer_form) + + def form_valid(self, form, answer_form): + self.object = form.save(commit=False) + self.object.applicant = self.request.user + self.object.job = Job.objects.get(id=self.kwargs['job_pk']) + self.object.save() + + answer_form.instance = self.object + answer_form.save() + + return HttpResponseRedirect(self.get_success_url()) + + def form_invalid(self, form, answer_form): + return self.render_to_response( + self.get_context_data(form=form, answer_form=answer_form))