From c8a68d542a7f1279757b577ad185995ad4602023 Mon Sep 17 00:00:00 2001 From: James Graham Date: Wed, 24 Feb 2021 14:59:43 +0000 Subject: [PATCH] feat: add organisation questions to update view --- people/forms.py | 52 +++++++++++++++++-- .../templates/people/organisation/detail.html | 4 ++ .../templates/people/organisation/update.html | 5 +- people/views/organisation.py | 38 +++++++++++++- 4 files changed, 89 insertions(+), 10 deletions(-) diff --git a/people/forms.py b/people/forms.py index 6ff2b37..53ae15d 100644 --- a/people/forms.py +++ b/people/forms.py @@ -57,7 +57,7 @@ class DynamicAnswerSetBase(forms.Form): field = field_class(label=question, queryset=question.answers, widget=field_widget, - required=self.field_required, + required=self.field_required and not question.allow_free_text, initial=initial.get(field_name, None)) self.fields[field_name] = field @@ -67,6 +67,48 @@ class DynamicAnswerSetBase(forms.Form): self.fields[f'{field_name}_free'] = free_field +class OrganisationAnswerSetForm(forms.ModelForm, DynamicAnswerSetBase): + """Form for variable organisation attributes. + + Dynamic fields inspired by https://jacobian.org/2010/feb/28/dynamic-form-generation/ + """ + class Meta: + model = models.OrganisationAnswerSet + fields = [] + + question_model = models.OrganisationQuestion + answer_model = models.OrganisationQuestionChoice + + def save(self, commit=True) -> models.OrganisationAnswerSet: + # Save model + self.instance = super().save(commit=False) + self.instance.organisation_id = self.initial['organisation_id'] + if commit: + self.instance.save() + # Need to call same_m2m manually since we use commit=False above + self.save_m2m() + + if commit: + # Save answers to questions + for key, value in self.cleaned_data.items(): + if key.startswith('question_') and value: + if key.endswith('_free'): + # Create new answer from free text + value, _ = self.answer_model.objects.get_or_create( + text=value, + question=self.question_model.objects.get( + pk=key.split('_')[1])) + + try: + self.instance.question_answers.add(value) + + except TypeError: + # Value is a QuerySet - multiple choice question + self.instance.question_answers.add(*value.all()) + + return self.instance + + class PersonAnswerSetForm(forms.ModelForm, DynamicAnswerSetBase): """Form for variable person attributes. @@ -101,7 +143,7 @@ class PersonAnswerSetForm(forms.ModelForm, DynamicAnswerSetBase): answer_model = models.PersonQuestionChoice def save(self, commit=True) -> models.PersonAnswerSet: - # Save Relationship model + # Save model self.instance = super().save(commit=False) self.instance.person_id = self.initial['person_id'] if commit: @@ -110,7 +152,7 @@ class PersonAnswerSetForm(forms.ModelForm, DynamicAnswerSetBase): self.save_m2m() if commit: - # Save answers to relationship questions + # Save answers to questions for key, value in self.cleaned_data.items(): if key.startswith('question_') and value: if key.endswith('_free'): @@ -146,11 +188,11 @@ class RelationshipAnswerSetForm(forms.ModelForm, DynamicAnswerSetBase): answer_model = models.RelationshipQuestionChoice def save(self, commit=True) -> models.RelationshipAnswerSet: - # Save Relationship model + # Save model self.instance = super().save(commit=commit) if commit: - # Save answers to relationship questions + # Save answers to questions for key, value in self.cleaned_data.items(): if key.startswith('question_') and value: if key.endswith('_free'): diff --git a/people/templates/people/organisation/detail.html b/people/templates/people/organisation/detail.html index 5d01d2e..a1e48d8 100644 --- a/people/templates/people/organisation/detail.html +++ b/people/templates/people/organisation/detail.html @@ -29,6 +29,10 @@
+ {% include 'people/person/includes/answer_set_full.html' %} + +
+

diff --git a/people/templates/people/organisation/update.html b/people/templates/people/organisation/update.html index 7b5aa44..1d75030 100644 --- a/people/templates/people/organisation/update.html +++ b/people/templates/people/organisation/update.html @@ -33,10 +33,7 @@ {% csrf_token %} {% load bootstrap4 %} - {% bootstrap_form form exclude='latitude,longitude' %} - - {% bootstrap_field form.latitude %} - {% bootstrap_field form.longitude %} + {% bootstrap_form form %} {% buttons %} diff --git a/people/views/organisation.py b/people/views/organisation.py index c67542d..9c59cbf 100644 --- a/people/views/organisation.py +++ b/people/views/organisation.py @@ -1,6 +1,7 @@ import typing from django.contrib.auth.mixins import LoginRequiredMixin +from django.utils import timezone from django.views.generic import CreateView, DetailView, ListView, UpdateView from people import forms, models @@ -30,6 +31,7 @@ class OrganisationDetailView(LoginRequiredMixin, DetailView): """Add map marker to context.""" context = super().get_context_data(**kwargs) + context['answer_set'] = self.object.current_answers context['map_markers'] = [{ 'name': self.object.name, 'lat': self.object.latitude, @@ -44,7 +46,7 @@ class OrganisationUpdateView(LoginRequiredMixin, UpdateView): model = models.Organisation context_object_name = 'organisation' template_name = 'people/organisation/update.html' - form_class = forms.OrganisationForm + form_class = forms.OrganisationAnswerSetForm def get_context_data(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: @@ -58,3 +60,37 @@ class OrganisationUpdateView(LoginRequiredMixin, UpdateView): }] return context + + def get_initial(self) -> typing.Dict[str, typing.Any]: + try: + previous_answers = self.object.current_answers.as_dict() + + except AttributeError: + previous_answers = {} + + previous_answers.update({ + 'organisation_id': self.object.id, + }) + + return previous_answers + + def get_form_kwargs(self) -> typing.Dict[str, typing.Any]: + """Remove instance from form kwargs as it's an Organisation, but expects an OrganisationAnswerSet.""" + kwargs = super().get_form_kwargs() + kwargs.pop('instance') + + return kwargs + + def form_valid(self, form): + """Mark any previous answer sets as replaced.""" + response = super().form_valid(form) + now_date = timezone.now().date() + + # Saving the form made self.object an OrganisationAnswerSet - so go up, then back down + # Shouldn't be more than one after initial updates after migration + for answer_set in self.object.organisation.answer_sets.exclude( + pk=self.object.pk): + answer_set.replaced_timestamp = now_date + answer_set.save() + + return response