From 7d4195fc6c0e7744775823e289ee61c980d352f4 Mon Sep 17 00:00:00 2001 From: Aatish Neupane Date: Wed, 17 Oct 2018 15:24:31 +0545 Subject: [PATCH] use crispy with bootstrap4 for forms, job creation view with autocomplete tags --- ipv6work/settings.py | 15 +++++++++++++++ jobs/autocomplete.py | 27 +++++++++++++++++++++++++++ jobs/forms.py | 21 +++++++++++++++++++++ jobs/models.py | 3 +++ jobs/templates/base.html | 2 +- jobs/templates/jobs/job_form.html | 12 ++++++++++++ jobs/urls.py | 14 +++++++++++++- jobs/views.py | 18 ++++++++++++++++-- requirements.txt | 4 +++- 9 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 jobs/autocomplete.py create mode 100644 jobs/forms.py create mode 100644 jobs/templates/jobs/job_form.html diff --git a/ipv6work/settings.py b/ipv6work/settings.py index c8260df..34196ff 100644 --- a/ipv6work/settings.py +++ b/ipv6work/settings.py @@ -39,6 +39,18 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', ] +# Libraries and other apps +INSTALLED_APPS += [ + 'crispy_forms', + + # Used for autocomplete and dynamic creation of tags. + # Ff this widget is required for admin, place these + # before 'django.contrib.admin' app + 'dal', + 'dal_select2', +] + +# Our apps INSTALLED_APPS += [ 'jobs', ] @@ -122,3 +134,6 @@ USE_TZ = True # https://docs.djangoproject.com/en/2.1/howto/static-files/ STATIC_URL = '/static/' + + +CRISPY_TEMPLATE_PACK = 'bootstrap4' diff --git a/jobs/autocomplete.py b/jobs/autocomplete.py new file mode 100644 index 0000000..f8e709e --- /dev/null +++ b/jobs/autocomplete.py @@ -0,0 +1,27 @@ +from dal import autocomplete + +from .models import Tag + + +class AuthenticatedUserCreateMixin(): + ''' By default, autocomplete-light only allows creation of + choices if the user has add permission on the model. However, + we need to allow any authenticated user to create tags + ''' + def has_add_permission(self, request): + print("i am called", request.user) + return request.user.is_authenticated + + +class TagAutocomplete( + AuthenticatedUserCreateMixin, autocomplete.Select2QuerySetView): + def get_queryset(self): + if not self.request.user.is_authenticated: + return Tag.objects.none() + + qs = Tag.objects.all() + + if self.q: + qs = qs.filter(name__icontains=self.q) + + return qs \ No newline at end of file diff --git a/jobs/forms.py b/jobs/forms.py new file mode 100644 index 0000000..eaff056 --- /dev/null +++ b/jobs/forms.py @@ -0,0 +1,21 @@ +from django import forms +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Submit +from dal.autocomplete import ModelSelect2Multiple + +from .models import Job + + +class JobForm(forms.ModelForm): + class Meta: + model = Job + fields = ('title', 'description', 'tags') + widgets = { + 'tags': ModelSelect2Multiple(url='jobs:tag-autocomplete') + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_method = 'post' + self.helper.add_input(Submit('submit', 'Post Job')) diff --git a/jobs/models.py b/jobs/models.py index 160c275..c56bbe8 100644 --- a/jobs/models.py +++ b/jobs/models.py @@ -9,6 +9,9 @@ User = get_user_model() class Tag(models.Model): name = models.CharField(max_length=255, unique=True, db_index=True) + class Meta: + ordering = ['name'] + def __str__(self): return self.name diff --git a/jobs/templates/base.html b/jobs/templates/base.html index 780fd50..5eafc96 100644 --- a/jobs/templates/base.html +++ b/jobs/templates/base.html @@ -18,7 +18,7 @@ Tags Jobs - Login + Post a job
diff --git a/jobs/templates/jobs/job_form.html b/jobs/templates/jobs/job_form.html new file mode 100644 index 0000000..a878737 --- /dev/null +++ b/jobs/templates/jobs/job_form.html @@ -0,0 +1,12 @@ +{% extends 'base.html' %} +{% block title %}Post a Job{% endblock %} + +{% load crispy_forms_tags %} + +{% block body_content %} +
+
+ {% crispy form %} +
+
+{% endblock %} \ No newline at end of file diff --git a/jobs/urls.py b/jobs/urls.py index e2bbe6a..65acb2c 100644 --- a/jobs/urls.py +++ b/jobs/urls.py @@ -1,9 +1,21 @@ from django.urls import path from . import views +from . import autocomplete as autocomplete_views app_name = 'jobs' urlpatterns = [ path('', views.Index.as_view(), name='index'), - path('jobs/', views.JobList.as_view(), name='jobs'), + path('jobs/create/', views.JobCreate.as_view(), name='create'), + path('jobs/', views.JobList.as_view(), name='list'), +] + + +# autocomplete endpoints +urlpatterns += [ + path( + 'tag-autocomplete/', + autocomplete_views.TagAutocomplete.as_view(create_field='name'), + name='tag-autocomplete', + ), ] \ No newline at end of file diff --git a/jobs/views.py b/jobs/views.py index 41dc36e..138dc58 100644 --- a/jobs/views.py +++ b/jobs/views.py @@ -1,7 +1,11 @@ -from django.shortcuts import render -from django.views.generic import TemplateView, ListView +from django.urls import reverse_lazy +from django.views.generic import ( + TemplateView, ListView, CreateView +) from .models import Job +from .forms import JobForm + class Index(TemplateView): template_name = 'jobs/index.html' @@ -11,3 +15,13 @@ class JobList(ListView): context_object_name = 'jobs' model = Job + +class JobCreate(CreateView): + model = Job + form_class = JobForm + success_url = reverse_lazy("jobs:list") + + def form_valid(self, form): + obj = form.save(commit=False) + obj.posted_by = self.request.user + return super().form_valid(form) diff --git a/requirements.txt b/requirements.txt index 81e6b2d..45e0996 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ -Django==2.1.2 \ No newline at end of file +Django==2.1.2 +django-crispy-forms==1.7.2 +git+https://github.com/yourlabs/django-autocomplete-light.git#egg=django-autocomplete-light