feat(people): Allow people to describe relationships

Creating a relationship requires answering each of the
relationship questions

resolve #2
partial #5
This commit is contained in:
James Graham
2020-02-20 14:46:51 +00:00
parent e1df999108
commit 5a1b043862
7 changed files with 147 additions and 1 deletions

View File

@@ -1,7 +1,6 @@
""" """
Forms for creating / updating models belonging to the 'people' app. Forms for creating / updating models belonging to the 'people' app.
""" """
from django import forms from django import forms
from . import models from . import models
@@ -17,3 +16,42 @@ class PersonForm(forms.ModelForm):
'name', 'name',
'core_member', 'core_member',
] ]
class RelationshipForm(forms.ModelForm):
"""
Form to allow users to describe a relationship - includes :class:`RelationshipQuestion`s.
Dynamic fields inspired by https://jacobian.org/2010/feb/28/dynamic-form-generation/
"""
class Meta:
model = models.Relationship
fields = [
'source',
'target',
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for question in models.RelationshipQuestion.objects.all():
# Get choices from model and add default 'not selected' option
choices = question.choices + [['', '---------']]
field = forms.ChoiceField(label=question,
choices=choices)
self.fields['question_{}'.format(question.pk)] = field
def save(self, commit=True) -> models.Relationship:
# 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_'):
question_pk = key.split('_')[-1]
answer = models.RelationshipQuestionChoice.objects.get(pk=value)
self.instance.question_answers.add(answer)
return self.instance

View File

@@ -0,0 +1,18 @@
# Generated by Django 2.2.10 on 2020-02-20 13:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('people', '0006_relationship_questions_order'),
]
operations = [
migrations.AddField(
model_name='relationship',
name='question_answers',
field=models.ManyToManyField(to='people.RelationshipQuestionChoice'),
),
]

View File

@@ -139,5 +139,11 @@ class Relationship(models.Model):
on_delete=models.CASCADE, on_delete=models.CASCADE,
blank=False, null=False) blank=False, null=False)
#: Answers to :class:`RelationshipQuestion`s
question_answers = models.ManyToManyField(RelationshipQuestionChoice)
def get_absolute_url(self):
return reverse('people:relationship.detail', kwargs={'pk': self.pk})
def __str__(self) -> str: def __str__(self) -> str:
return f'{self.source} -> {self.target}' return f'{self.source} -> {self.target}'

View File

@@ -14,6 +14,10 @@
<h2>Relationships As Source</h2> <h2>Relationships As Source</h2>
<a class="btn btn-success"
href="{% url 'people:person.relationship.create' person_pk=person.pk %}">New Relationship
</a>
<table class="table table-borderless"> <table class="table table-borderless">
<thead> <thead>
<tr> <tr>

View File

@@ -0,0 +1,30 @@
{% extends 'base.html' %}
{% block content %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{% url 'people:person.list' %}">People</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'people:person.detail' pk=person.pk %}">{{ person }}</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Create Relationship</li>
</ol>
</nav>
<hr>
<form class="form"
method="POST">
{% csrf_token %}
{% load bootstrap4 %}
{% bootstrap_form form %}
{% buttons %}
<button class="btn btn-success" type="submit">Submit</button>
{% endbuttons %}
</form>
{% endblock %}

View File

@@ -22,6 +22,10 @@ urlpatterns = [
views.ProfileView.as_view(), views.ProfileView.as_view(),
name='person.detail'), name='person.detail'),
path('people/<int:person_pk>/relationships/create',
views.RelationshipCreateView.as_view(),
name='person.relationship.create'),
path('relationships/<int:pk>', path('relationships/<int:pk>',
views.RelationshipDetailView.as_view(), views.RelationshipDetailView.as_view(),
name='relationship.detail'), name='relationship.detail'),

View File

@@ -2,6 +2,7 @@
Views for displaying or manipulating models in the 'people' app. Views for displaying or manipulating models in the 'people' app.
""" """
from django.http import HttpResponseRedirect
from django.views.generic import CreateView, DetailView, ListView from django.views.generic import CreateView, DetailView, ListView
from . import forms, models from . import forms, models
@@ -58,3 +59,48 @@ class RelationshipDetailView(DetailView):
""" """
model = models.Relationship model = models.Relationship
template_name = 'people/relationship/detail.html' template_name = 'people/relationship/detail.html'
class RelationshipCreateView(CreateView):
"""
View for creating a :class:`Relationship`.
Displays / processes a form containing the :class:`RelationshipQuestion`s.
"""
model = models.Relationship
template_name = 'people/relationship/create.html'
form_class = forms.RelationshipForm
def get(self, request, *args, **kwargs):
self.person = models.Person.objects.get(pk=self.kwargs.get('person_pk'))
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.person = models.Person.objects.get(pk=self.kwargs.get('person_pk'))
return super().post(request, *args, **kwargs)
def get_initial(self):
initial = super().get_initial()
initial['source'] = self.request.user.person
initial['target'] = self.person
return initial
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['person'] = self.person
return context
def form_valid(self, form):
"""
Form is valid - create :class:`Relationship` and save answers to questions.
"""
self.object = form.save()
return HttpResponseRedirect(self.object.get_absolute_url())