fix: Only allow users to create rels as source

There is now no field for users to define the source of a relationship
The source is always the person in the URL
And only that user or staff can access the form
This commit is contained in:
James Graham
2020-08-14 17:38:22 +01:00
parent 6435ec69a1
commit a94db2713e
4 changed files with 50 additions and 39 deletions

View File

@@ -19,7 +19,11 @@ from django.urls import include, path
from . import views from . import views
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/',
admin.site.urls),
path('select2/',
include('django_select2.urls')),
path('', path('',
include('django.contrib.auth.urls')), include('django.contrib.auth.urls')),
@@ -36,4 +40,4 @@ urlpatterns = [
path('', path('',
include('activities.urls')), include('activities.urls')),
] ] # yapf: disable

View File

@@ -8,7 +8,7 @@ from django import forms
from django.forms.widgets import SelectDateWidget from django.forms.widgets import SelectDateWidget
from django.utils import timezone from django.utils import timezone
from django_select2.forms import Select2Widget, Select2MultipleWidget from django_select2.forms import ModelSelect2Widget, Select2Widget, Select2MultipleWidget
from . import models from . import models
@@ -60,6 +60,12 @@ class PersonForm(forms.ModelForm):
years=get_date_year_range()) years=get_date_year_range())
class RelationshipForm(forms.Form):
target = forms.ModelChoiceField(
models.Person.objects.all(),
widget=ModelSelect2Widget(search_fields=['name__icontains']))
class DynamicAnswerSetBase(forms.Form): class DynamicAnswerSetBase(forms.Form):
field_class = forms.ModelChoiceField field_class = forms.ModelChoiceField
field_widget = None field_widget = None

View File

@@ -23,7 +23,9 @@ class UserIsLinkedPersonMixin(UserPassesTestMixin):
test_person = self.get_object() test_person = self.get_object()
if not isinstance(test_person, models.Person): if not isinstance(test_person, models.Person):
raise AttributeError('View incorrectly configured: \'related_person_field\' must be defined.') raise AttributeError(
'View incorrectly configured: \'related_person_field\' must be defined.'
)
return test_person return test_person
@@ -34,4 +36,5 @@ class UserIsLinkedPersonMixin(UserPassesTestMixin):
Require that user is either staff or is the linked person. Require that user is either staff or is the linked person.
""" """
user = self.request.user user = self.request.user
return user.is_authenticated and (user.is_staff or self.get_test_person() == user.person) return user.is_authenticated and (
user.is_staff or self.get_test_person() == user.person)

View File

@@ -2,9 +2,11 @@
Views for displaying or manipulating instances of :class:`Relationship`. Views for displaying or manipulating instances of :class:`Relationship`.
""" """
from django.db import IntegrityError
from django.forms import ValidationError
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.views.generic import CreateView, DetailView from django.views.generic import CreateView, DetailView, FormView
from people import forms, models, permissions from people import forms, models, permissions
@@ -18,7 +20,7 @@ class RelationshipDetailView(permissions.UserIsLinkedPersonMixin, DetailView):
related_person_field = 'source' related_person_field = 'source'
class RelationshipCreateView(permissions.UserIsLinkedPersonMixin, CreateView): class RelationshipCreateView(permissions.UserIsLinkedPersonMixin, FormView):
""" """
View for creating a :class:`Relationship`. View for creating a :class:`Relationship`.
@@ -26,53 +28,45 @@ class RelationshipCreateView(permissions.UserIsLinkedPersonMixin, CreateView):
""" """
model = models.Relationship model = models.Relationship
template_name = 'people/relationship/create.html' template_name = 'people/relationship/create.html'
fields = [ form_class = forms.RelationshipForm
'source',
'target',
]
def get_test_person(self) -> models.Person:
"""
Get the person instance which should be used for access control checks.
"""
if self.request.method == 'POST':
return models.Person.objects.get(pk=self.request.POST.get('source'))
def get_person(self) -> models.Person:
return models.Person.objects.get(pk=self.kwargs.get('person_pk')) return models.Person.objects.get(pk=self.kwargs.get('person_pk'))
def get(self, request, *args, **kwargs): def get_test_person(self) -> models.Person:
self.person = models.Person.objects.get(pk=self.kwargs.get('person_pk')) return self.get_person()
return super().get(request, *args, **kwargs) def form_valid(self, form):
try:
self.object = models.Relationship.objects.create(
source=self.get_person(), target=form.cleaned_data['target'])
def post(self, request, *args, **kwargs): except IntegrityError:
self.person = models.Person.objects.get(pk=self.kwargs.get('person_pk')) form.add_error(
None,
ValidationError('This relationship already exists',
code='already-exists'))
return self.form_invalid(form)
return super().post(request, *args, **kwargs) return super().form_valid(form)
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): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['person'] = self.person context['person'] = self.get_person()
return context return context
def get_success_url(self): def get_success_url(self):
return reverse('people:relationship.update', kwargs={'relationship_pk': self.object.pk}) return reverse('people:relationship.update',
kwargs={'relationship_pk': self.object.pk})
class RelationshipUpdateView(permissions.UserIsLinkedPersonMixin, CreateView): class RelationshipUpdateView(permissions.UserIsLinkedPersonMixin, CreateView):
""" """
View for creating a :class:`Relationship`. View for updating the details of a relationship.
Creates a new :class:`RelationshipAnswerSet` for the :class:`Relationship`.
Displays / processes a form containing the :class:`RelationshipQuestion`s. Displays / processes a form containing the :class:`RelationshipQuestion`s.
""" """
model = models.RelationshipAnswerSet model = models.RelationshipAnswerSet
@@ -83,18 +77,21 @@ class RelationshipUpdateView(permissions.UserIsLinkedPersonMixin, CreateView):
""" """
Get the person instance which should be used for access control checks. Get the person instance which should be used for access control checks.
""" """
relationship = models.Relationship.objects.get(pk=self.kwargs.get('relationship_pk')) relationship = models.Relationship.objects.get(
pk=self.kwargs.get('relationship_pk'))
return relationship.source return relationship.source
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.relationship = models.Relationship.objects.get(pk=self.kwargs.get('relationship_pk')) self.relationship = models.Relationship.objects.get(
pk=self.kwargs.get('relationship_pk'))
self.person = self.relationship.source self.person = self.relationship.source
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.relationship = models.Relationship.objects.get(pk=self.kwargs.get('relationship_pk')) self.relationship = models.Relationship.objects.get(
pk=self.kwargs.get('relationship_pk'))
self.person = self.relationship.source self.person = self.relationship.source
return super().post(request, *args, **kwargs) return super().post(request, *args, **kwargs)
@@ -122,7 +119,8 @@ class RelationshipUpdateView(permissions.UserIsLinkedPersonMixin, CreateView):
now_date = timezone.now().date() now_date = timezone.now().date()
# Shouldn't be more than one after initial updates after migration # Shouldn't be more than one after initial updates after migration
for answer_set in self.relationship.answer_sets.exclude(pk=self.object.pk): for answer_set in self.relationship.answer_sets.exclude(
pk=self.object.pk):
answer_set.replaced_timestamp = now_date answer_set.replaced_timestamp = now_date
answer_set.save() answer_set.save()