Restrict ticket selection for dependencies and parents

When adding a dependent or parent ticket, the choice field in the
ticket selection form excludes:

- All existing dependencies of the current ticket.
- All existing parent tickets of the current ticket.
- The current ticket itself.

The first two prevent immediate circular references: A dependency
cannot be a parent and vice versa.  Deeper circular references are
not covered by this: a ticket can still be its own grandchild.

They also prevent current behavior of throwing an `IntegrityException`
when selecting a dependency or parent.

The third one prevents also a quirky behavior: until now, specifying the
ticket itself as parent or dependency just does not save the
dependency and does not issue a warning either.
This commit is contained in:
Georg Lehner 2024-06-08 12:11:01 +02:00
parent b52ab69c42
commit 25c36a6f5c
2 changed files with 16 additions and 4 deletions

View File

@ -591,6 +591,12 @@ class TicketDependencyForm(forms.ModelForm):
model = TicketDependency model = TicketDependency
exclude = ('ticket',) exclude = ('ticket',)
def __init__(self, ticket, *args, **kwargs):
super(TicketDependencyForm,self).__init__(*args, **kwargs)
# Exclude duplicate tickets, myself, existing dependencies and parents
self.fields['depends_on'].queryset = Ticket.objects.exclude(status=Ticket.DUPLICATE_STATUS).exclude(id=ticket.id).exclude(depends_on__ticket=ticket).exclude(ticketdependency__depends_on=ticket)
class TicketResolvesForm(forms.ModelForm): class TicketResolvesForm(forms.ModelForm):
''' Adds this ticket as a dependency for a different ticket ''' ''' Adds this ticket as a dependency for a different ticket '''
@ -599,6 +605,12 @@ class TicketResolvesForm(forms.ModelForm):
#exclude = ('depends_on',) #exclude = ('depends_on',)
fields = ('ticket',) fields = ('ticket',)
def __init__(self, ticket, *args, **kwargs):
super(TicketResolvesForm,self).__init__(*args, **kwargs)
# Exclude duplicate tickets, myself, existing dependencies and parents
self.fields['ticket'].queryset = Ticket.objects.exclude(status=Ticket.DUPLICATE_STATUS).exclude(id=ticket.id).exclude(depends_on__ticket=ticket).exclude(ticketdependency__depends_on=ticket)
class MultipleTicketSelectForm(forms.Form): class MultipleTicketSelectForm(forms.Form):
tickets = forms.ModelMultipleChoiceField( tickets = forms.ModelMultipleChoiceField(

View File

@ -1672,7 +1672,7 @@ def ticket_dependency_add(request, ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id) ticket = get_object_or_404(Ticket, id=ticket_id)
ticket_perm_check(request, ticket) ticket_perm_check(request, ticket)
if request.method == 'POST': if request.method == 'POST':
form = TicketDependencyForm(request.POST) form = TicketDependencyForm(ticket, request.POST)
if form.is_valid(): if form.is_valid():
ticketdependency = form.save(commit=False) ticketdependency = form.save(commit=False)
ticketdependency.ticket = ticket ticketdependency.ticket = ticket
@ -1680,7 +1680,7 @@ def ticket_dependency_add(request, ticket_id):
ticketdependency.save() ticketdependency.save()
return HttpResponseRedirect(reverse('helpdesk:view', args=[ticket.id])) return HttpResponseRedirect(reverse('helpdesk:view', args=[ticket.id]))
else: else:
form = TicketDependencyForm() form = TicketDependencyForm(ticket)
return render(request, 'helpdesk/ticket_dependency_add.html', { return render(request, 'helpdesk/ticket_dependency_add.html', {
'ticket': ticket, 'ticket': ticket,
'form': form, 'form': form,
@ -1708,7 +1708,7 @@ def ticket_resolves_add(request, ticket_id):
depends_on = get_object_or_404(Ticket, id=ticket_id) depends_on = get_object_or_404(Ticket, id=ticket_id)
ticket_perm_check(request, depends_on) ticket_perm_check(request, depends_on)
if request.method == 'POST': if request.method == 'POST':
form = TicketResolvesForm(request.POST) form = TicketResolvesForm(depends_on, request.POST)
if form.is_valid(): if form.is_valid():
ticketdependency = form.save(commit=False) ticketdependency = form.save(commit=False)
ticketdependency.depends_on = depends_on ticketdependency.depends_on = depends_on
@ -1716,7 +1716,7 @@ def ticket_resolves_add(request, ticket_id):
ticketdependency.save() ticketdependency.save()
return HttpResponseRedirect(reverse('helpdesk:view', args=[depends_on.id])) return HttpResponseRedirect(reverse('helpdesk:view', args=[depends_on.id]))
else: else:
form = TicketResolvesForm() form = TicketResolvesForm(depends_on)
return render(request, 'helpdesk/ticket_resolves_add.html', { return render(request, 'helpdesk/ticket_resolves_add.html', {
'depends_on': depends_on, 'depends_on': depends_on,
'form': form, 'form': form,