Fix cable edit form with single unterminated cable

This commit is contained in:
Daniel Sheppard 2024-04-23 14:03:34 -05:00
parent 85db007ff5
commit 91efa14dbb
3 changed files with 59 additions and 12 deletions

View File

@ -23,6 +23,7 @@ from netbox.constants import DEFAULT_ACTION_PERMISSIONS
from netbox.views import generic from netbox.views import generic
from tenancy.views import ObjectContactsView from tenancy.views import ObjectContactsView
from utilities.forms import ConfirmationForm from utilities.forms import ConfirmationForm
from utilities.htmx import is_htmx
from utilities.paginator import EnhancedPaginator, get_paginate_count from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.permissions import get_permission_for_model from utilities.permissions import get_permission_for_model
from utilities.query_functions import CollateAsChar from utilities.query_functions import CollateAsChar
@ -3183,6 +3184,7 @@ class CableView(generic.ObjectView):
class CableEditView(generic.ObjectEditView): class CableEditView(generic.ObjectEditView):
queryset = Cable.objects.all() queryset = Cable.objects.all()
template_name = 'dcim/cable_edit.html' template_name = 'dcim/cable_edit.html'
htmx_template_name = 'dcim/cable_edit.html'
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
@ -3195,22 +3197,29 @@ class CableEditView(generic.ObjectEditView):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get_object(self, **kwargs): def alter_object(self, obj, request, url_args, url_kwargs):
""" """
Hack into get_object() to set the form class when editing an existing Cable, since ObjectEditView Hack into get_object() to set the form class when editing an existing Cable, since ObjectEditView
doesn't currently provide a hook for dynamic class resolution. doesn't currently provide a hook for dynamic class resolution.
""" """
obj = super().get_object(**kwargs) a_terminations_type = request.GET.get('a_terminations_type')
b_terminations_type = request.GET.get('b_terminations_type')
if obj.pk: if obj.pk:
# TODO: Optimize this logic # TODO: Optimize this logic
termination_a = obj.terminations.filter(cable_end='A').first() termination_a = obj.terminations.filter(cable_end='A').first()
a_type = termination_a.termination._meta.model if termination_a else None a_type = termination_a.termination._meta.model if termination_a else (
CABLE_TERMINATION_TYPES.get(a_terminations_type)
)
termination_b = obj.terminations.filter(cable_end='B').first() termination_b = obj.terminations.filter(cable_end='B').first()
b_type = termination_b.termination._meta.model if termination_b else None b_type = termination_b.termination._meta.model if termination_b else (
CABLE_TERMINATION_TYPES.get(b_terminations_type)
)
self.skip_htmx = True
self.form = forms.get_cable_form(a_type, b_type) self.form = forms.get_cable_form(a_type, b_type)
return obj return super().alter_object(obj, request, url_args, url_kwargs)
def get_extra_addanother_params(self, request): def get_extra_addanother_params(self, request):

View File

@ -167,6 +167,7 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView):
""" """
template_name = 'generic/object_edit.html' template_name = 'generic/object_edit.html'
form = None form = None
skip_htmx = False
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
# Determine required permission based on whether we are editing an existing object # Determine required permission based on whether we are editing an existing object
@ -227,7 +228,7 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView):
restrict_form_fields(form, request.user) restrict_form_fields(form, request.user)
# If this is an HTMX request, return only the rendered form HTML # If this is an HTMX request, return only the rendered form HTML
if is_htmx(request): if is_htmx(request) and not self.skip_htmx:
return render(request, 'htmx/form.html', { return render(request, 'htmx/form.html', {
'form': form, 'form': form,
}) })

View File

@ -5,9 +5,8 @@
{% load i18n %} {% load i18n %}
{% block form %} {% block form %}
{# A side termination #} {# A side termination #}
<div class="field-group mb-5"> <div id="a_termination_block" class="field-group mb-5">
<div class="row mb-2"> <div class="row mb-2">
<h5 class="offset-sm-3">{% trans "A Side" %}</h5> <h5 class="offset-sm-3">{% trans "A Side" %}</h5>
</div> </div>
@ -20,11 +19,30 @@
{% if 'termination_a_circuit' in form.fields %} {% if 'termination_a_circuit' in form.fields %}
{% render_field form.termination_a_circuit %} {% render_field form.termination_a_circuit %}
{% endif %} {% endif %}
{% render_field form.a_terminations %} {% if 'a_terminations' in form.fields %}
{% render_field form.a_terminations %}
{% elif 'b_terminations' in form.fields %}
<div class="row mb-3">
<label class="col-sm-3 col-form-label text-lg-end">{% trans "Termination Type" %}</label>
<div class="col" hx-select="#a_termination_block" hx-target="#a_termination_block">
<button class="btn btn-primary" hx-get="?a_terminations_type=dcim.interface">{% trans "Interface" %}</button>
<button class="btn btn-primary" hx-get="?a_terminations_type=dcim.frontport">{% trans "Front Port" %}</button>
<button class="btn btn-primary" hx-get="?a_terminations_type=dcim.rearport">{% trans "Rear Port" %}</button>
<button class="btn btn-primary" hx-get="?a_terminations_type=circuits.circuittermination">{% trans "Circuit Termination" %}</button>
</div>
</div>
{% else %}
<div class="row mb-3">
<label for="a_terminations" class="col-sm-3 col-form-label text-lg-end">{% trans "Termination Type" %}</label>
<div class="col col-form-label">Cannot initialize dynamic cable termination selection form</div>
<select class="ts-hidden-accessible" name="a_terminations" required></select>
<div class="invalid-feedback">This field is required.</div>
</div>
{% endif %}
</div> </div>
{# B side termination #} {# B side termination #}
<div class="field-group mb-5"> <div id="b_termination_block" class="field-group mb-5">
<div class="row mb-2"> <div class="row mb-2">
<h5 class="offset-sm-3">{% trans "B Side" %}</h5> <h5 class="offset-sm-3">{% trans "B Side" %}</h5>
</div> </div>
@ -37,7 +55,26 @@
{% if 'termination_b_circuit' in form.fields %} {% if 'termination_b_circuit' in form.fields %}
{% render_field form.termination_b_circuit %} {% render_field form.termination_b_circuit %}
{% endif %} {% endif %}
{% render_field form.b_terminations %} {% if 'b_terminations' in form.fields %}
{% render_field form.b_terminations %}
{% elif 'a_terminations' in form.fields %}
<div class="row mb-3">
<label class="col-sm-3 col-form-label text-lg-end">{% trans "Termination Type" %}</label>
<div class="col" hx-select="#b_termination_block" hx-target="#b_termination_block">
<button class="btn btn-primary" hx-get="?b_terminations_type=dcim.interface">{% trans "Interface" %}</button>
<button class="btn btn-primary" hx-get="?b_terminations_type=dcim.frontport">{% trans "Front Port" %}</button>
<button class="btn btn-primary" hx-get="?b_terminations_type=dcim.rearport">{% trans "Rear Port" %}</button>
<button class="btn btn-primary" hx-get="?b_terminations_type=circuits.circuittermination">{% trans "Circuit Termination" %}</button>
</div>
</div>
{% else %}
<div class="row mb-3">
<label for="b_terminantions" class="col-sm-3 col-form-label text-lg-end">{% trans "Termination Type" %}</label>
<div class="col col-form-label">Cannot initialize dynamic cable termination selection form</div>
<select class="ts-hidden-accessible" name="b_terminations" required></select>
<div class="invalid-feedback">This field is required.</div>
</div>
{% endif %}
</div> </div>
{# Cable attributes #} {# Cable attributes #}