From 6bb4f09454ddef135fe5a32e99b04319036b2c57 Mon Sep 17 00:00:00 2001 From: James Graham Date: Wed, 2 Dec 2020 15:53:30 +0000 Subject: [PATCH] refactor: update forms to match moved questions --- people/forms.py | 66 ++++++++++++++++++++++++++++++------------ people/views/person.py | 42 +++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 24 deletions(-) diff --git a/people/forms.py b/people/forms.py index 5e6a2a1..38561aa 100644 --- a/people/forms.py +++ b/people/forms.py @@ -25,29 +25,12 @@ def get_date_year_range() -> typing.Iterable[int]: class PersonForm(forms.ModelForm): - """ - Form for creating / updating an instance of :class:`Person`. - """ + """Form for creating / updating an instance of :class:`Person`.""" class Meta: model = models.Person fields = [ 'name', ] - widgets = { - 'nationality': Select2Widget(), - 'country_of_residence': Select2Widget(), - 'themes': Select2MultipleWidget(), - } - help_texts = { - 'organisation_started_date': - 'If you don\'t know the exact date, an approximate date is okay.', - } - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.fields['organisation_started_date'].widget = SelectDateWidget( - years=get_date_year_range()) class RelationshipForm(forms.Form): @@ -60,11 +43,12 @@ class DynamicAnswerSetBase(forms.Form): field_class = forms.ModelChoiceField field_widget = None field_required = True + question_model = None def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - for question in models.RelationshipQuestion.objects.all(): + for question in self.question_model.objects.all(): field = self.field_class(label=question, queryset=question.answers, widget=self.field_widget, @@ -72,6 +56,47 @@ class DynamicAnswerSetBase(forms.Form): self.fields['question_{}'.format(question.pk)] = field +class PersonAnswerSetForm(forms.ModelForm, DynamicAnswerSetBase): + """Form for variable person attributes. + + Dynamic fields inspired by https://jacobian.org/2010/feb/28/dynamic-form-generation/ + """ + class Meta: + model = models.PersonAnswerSet + fields = [ + 'nationality', + 'country_of_residence', + 'organisation', + 'organisation_started_date', + 'job_title', + 'disciplines', + 'themes', + ] + widgets = { + 'nationality': Select2Widget(), + 'country_of_residence': Select2Widget(), + 'themes': Select2MultipleWidget(), + } + help_texts = { + 'organisation_started_date': + 'If you don\'t know the exact date, an approximate date is okay.', + } + + question_model = models.PersonQuestion + + def save(self, commit=True) -> models.PersonAnswerSet: + # Save Relationship model + self.instance = super().save(commit=commit) + + if commit: + # Save answers to relationship questions + for key, value in self.cleaned_data.items(): + if key.startswith('question_') and value: + self.instance.question_answers.add(value) + + return self.instance + + class RelationshipAnswerSetForm(forms.ModelForm, DynamicAnswerSetBase): """ Form to allow users to describe a relationship. @@ -84,6 +109,8 @@ class RelationshipAnswerSetForm(forms.ModelForm, DynamicAnswerSetBase): 'relationship', ] + question_model = models.RelationshipQuestion + def save(self, commit=True) -> models.RelationshipAnswerSet: # Save Relationship model self.instance = super().save(commit=commit) @@ -104,6 +131,7 @@ class NetworkFilterForm(DynamicAnswerSetBase): field_class = forms.ModelMultipleChoiceField field_widget = Select2MultipleWidget field_required = False + question_model = models.RelationshipQuestion def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/people/views/person.py b/people/views/person.py index 92e7ee9..c495a86 100644 --- a/people/views/person.py +++ b/people/views/person.py @@ -3,6 +3,7 @@ Views for displaying or manipulating instances of :class:`Person`. """ 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, permissions @@ -55,9 +56,40 @@ class ProfileView(permissions.UserIsLinkedPersonMixin, DetailView): class PersonUpdateView(permissions.UserIsLinkedPersonMixin, UpdateView): - """ - View for updating a :class:`Person` record. - """ - model = models.Person + """View for updating a :class:`Person` record.""" + model = models.PersonAnswerSet template_name = 'people/person/update.html' - form_class = forms.PersonForm + form_class = forms.PersonAnswerSetForm + + def get_test_person(self) -> models.Person: + """Get the person instance which should be used for access control checks.""" + return models.Person.objects.get(pk=self.kwargs.get('pk')) + + def get(self, request, *args, **kwargs): + self.person = models.Person.objects.get(pk=self.kwargs.get('pk')) + + return super().get(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + self.person = models.Person.objects.get(pk=self.kwargs.get('pk')) + + return super().post(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + context['person'] = self.person + + return context + + def form_valid(self, form): + """Mark any previous answer sets as replaced.""" + response = super().form_valid(form) + now_date = timezone.now().date() + + # Shouldn't be more than one after initial updates after migration + for answer_set in self.person.answer_sets.exclude(pk=self.object.pk): + answer_set.replaced_timestamp = now_date + answer_set.save() + + return response