From 4cfee6362d11651a3e74e3dc295b61dc51192c10 Mon Sep 17 00:00:00 2001 From: James Graham Date: Fri, 15 Jan 2021 17:45:55 +0000 Subject: [PATCH] refactor: add person update form fields to initial --- people/forms.py | 19 +++++++++-------- people/models/person.py | 45 +++++++++++++++++++++++++++++++++++++++++ people/views/person.py | 12 +++++++++-- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/people/forms.py b/people/forms.py index d1495d9..607214f 100644 --- a/people/forms.py +++ b/people/forms.py @@ -1,6 +1,4 @@ -""" -Forms for creating / updating models belonging to the 'people' app. -""" +"""Forms for creating / updating models belonging to the 'people' app.""" import typing @@ -28,11 +26,7 @@ class OrganisationForm(forms.ModelForm): """Form for creating / updating an instance of :class:`Organisation`.""" class Meta: model = models.Organisation - fields = [ - 'name', - 'latitude', - 'longitude' - ] + fields = ['name', 'latitude', 'longitude'] class PersonForm(forms.ModelForm): @@ -59,6 +53,8 @@ class DynamicAnswerSetBase(forms.Form): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + initial = kwargs.get('initial', {}) + for question in self.question_model.objects.all(): field_class = self.field_class field_widget = self.field_widget @@ -67,11 +63,14 @@ class DynamicAnswerSetBase(forms.Form): field_class = forms.ModelMultipleChoiceField field_widget = Select2MultipleWidget + field_name = f'question_{question.pk}' + field = field_class(label=question, queryset=question.answers, widget=field_widget, - required=self.field_required) - self.fields['question_{}'.format(question.pk)] = field + required=self.field_required, + initial=initial.get(field_name, None)) + self.fields[field_name] = field class PersonAnswerSetForm(forms.ModelForm, DynamicAnswerSetBase): diff --git a/people/models/person.py b/people/models/person.py index a472f82..d0b8509 100644 --- a/people/models/person.py +++ b/people/models/person.py @@ -190,5 +190,50 @@ class PersonAnswerSet(AnswerSet): #: Longitude for displaying location on a map longitude = models.FloatField(blank=True, null=True) + def as_dict(self): + """Get the answers from this set as a dictionary for use in Form.initial.""" + exclude_fields = { + 'id', + 'timestemp', + 'replaced_timestamp', + 'person_id', + 'question_answers', + 'themes', + } + + def field_value_repr(field): + """Get the representation of a field's value as required by Form.initial.""" + attr_val = getattr(self, field.attname) + + # Relation fields need to return PKs + if isinstance(field, models.ManyToManyField): + return [obj.pk for obj in attr_val.all()] + + if isinstance(field, models.ForeignKey): + return attr_val.pk + + return attr_val + + answers = { + field.attname: field_value_repr(field) + for field in self._meta.get_fields() + if field.attname not in exclude_fields + } + + for answer in self.question_answers.all(): + question = answer.question + field_name = f'question_{question.pk}' + + if question.is_multiple_choice: + if field_name not in answers: + answers[field_name] = [] + + answers[field_name].append(answer.pk) + + else: + answers[field_name] = answer.pk + + return answers + def get_absolute_url(self): return self.person.get_absolute_url() diff --git a/people/views/person.py b/people/views/person.py index 28e26f9..73f2e3b 100644 --- a/people/views/person.py +++ b/people/views/person.py @@ -84,9 +84,17 @@ class PersonUpdateView(permissions.UserIsLinkedPersonMixin, UpdateView): return context def get_initial(self) -> typing.Dict[str, typing.Any]: - return { + try: + previous_answers = self.object.current_answers.as_dict() + + except AttributeError: + previous_answers = {} + + previous_answers.update({ 'person_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 a person, but expects a PersonAnswerSet."""