Skip to content

Commit 545769a

Browse files
Adds generic object children template (#13388)
* adds generic tab view template #12110 * Rename view_tab.html and move to generic/ * Fix console ports template * Move bulk operations view resolution to template * Avoid setting default template_name on ObjectChildrenView * Move base_template and table_config context vars to base context * removed bulk_delete_control from templates * refactored bulk_controls view * fixed table_config * renamed object_tab.html to objectchildren_list.html * removed unused import * Refactor template blocks for bulk operation buttons * Rename object children generic template * Move disconnect bulk action into a separate template for device components * Fix cluster devices & VM interfaces views * minor button label change --------- Co-authored-by: Jeremy Stretch <[email protected]>
1 parent f5a1f83 commit 545769a

32 files changed

+331
-1003
lines changed

netbox/dcim/views.py

+13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import traceback
2+
from collections import defaultdict
23

34
from django.contrib import messages
45
from django.contrib.contenttypes.models import ContentType
@@ -45,6 +46,15 @@
4546

4647

4748
class DeviceComponentsView(generic.ObjectChildrenView):
49+
actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete', 'bulk_rename', 'bulk_disconnect')
50+
action_perms = defaultdict(set, **{
51+
'add': {'add'},
52+
'import': {'add'},
53+
'bulk_edit': {'change'},
54+
'bulk_delete': {'delete'},
55+
'bulk_rename': {'change'},
56+
'bulk_disconnect': {'change'},
57+
})
4858
queryset = Device.objects.all()
4959

5060
def get_children(self, request, parent):
@@ -1997,6 +2007,7 @@ class DeviceModuleBaysView(DeviceComponentsView):
19972007
table = tables.DeviceModuleBayTable
19982008
filterset = filtersets.ModuleBayFilterSet
19992009
template_name = 'dcim/device/modulebays.html'
2010+
actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete', 'bulk_rename')
20002011
tab = ViewTab(
20012012
label=_('Module Bays'),
20022013
badge=lambda obj: obj.modulebays.count(),
@@ -2012,6 +2023,7 @@ class DeviceDeviceBaysView(DeviceComponentsView):
20122023
table = tables.DeviceDeviceBayTable
20132024
filterset = filtersets.DeviceBayFilterSet
20142025
template_name = 'dcim/device/devicebays.html'
2026+
actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete', 'bulk_rename')
20152027
tab = ViewTab(
20162028
label=_('Device Bays'),
20172029
badge=lambda obj: obj.devicebays.count(),
@@ -2023,6 +2035,7 @@ class DeviceDeviceBaysView(DeviceComponentsView):
20232035

20242036
@register_model_view(Device, 'inventory')
20252037
class DeviceInventoryView(DeviceComponentsView):
2038+
actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete', 'bulk_rename')
20262039
child_model = InventoryItem
20272040
table = tables.DeviceInventoryItemTable
20282041
filterset = filtersets.InventoryItemFilterSet

netbox/ipam/views.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ class ASNRangeASNsView(generic.ObjectChildrenView):
216216
child_model = ASN
217217
table = tables.ASNTable
218218
filterset = filtersets.ASNFilterSet
219-
template_name = 'ipam/asnrange/asns.html'
219+
template_name = 'generic/object_children.html'
220220
tab = ViewTab(
221221
label=_('ASNs'),
222222
badge=lambda x: x.get_child_asns().count(),
@@ -816,7 +816,6 @@ def post(self, request):
816816
table = None
817817

818818
if form.is_valid():
819-
820819
addresses = self.queryset.prefetch_related('vrf', 'tenant')
821820
# Limit to 100 results
822821
addresses = filtersets.IPAddressFilterSet(request.POST, addresses).qs[:100]
@@ -866,7 +865,7 @@ class IPAddressRelatedIPsView(generic.ObjectChildrenView):
866865
child_model = IPAddress
867866
table = tables.IPAddressTable
868867
filterset = filtersets.IPAddressFilterSet
869-
template_name = 'ipam/ipaddress/ip_addresses.html'
868+
template_name = 'generic/object_children.html'
870869
tab = ViewTab(
871870
label=_('Related IPs'),
872871
badge=lambda x: x.get_related_ips().count(),
@@ -963,7 +962,6 @@ class FHRPGroupView(generic.ObjectView):
963962
queryset = FHRPGroup.objects.all()
964963

965964
def get_extra_context(self, request, instance):
966-
967965
# Get assigned interfaces
968966
members_table = tables.FHRPGroupAssignmentTable(
969967
data=FHRPGroupAssignment.objects.restrict(request.user, 'view').filter(group=instance),
@@ -1077,7 +1075,7 @@ class VLANInterfacesView(generic.ObjectChildrenView):
10771075
child_model = Interface
10781076
table = tables.VLANDevicesTable
10791077
filterset = InterfaceFilterSet
1080-
template_name = 'ipam/vlan/interfaces.html'
1078+
template_name = 'generic/object_children.html'
10811079
tab = ViewTab(
10821080
label=_('Device Interfaces'),
10831081
badge=lambda x: x.get_interfaces().count(),
@@ -1095,7 +1093,7 @@ class VLANVMInterfacesView(generic.ObjectChildrenView):
10951093
child_model = VMInterface
10961094
table = tables.VLANVirtualMachinesTable
10971095
filterset = VMInterfaceFilterSet
1098-
template_name = 'ipam/vlan/vminterfaces.html'
1096+
template_name = 'generic/object_children.html'
10991097
tab = ViewTab(
11001098
label=_('VM Interfaces'),
11011099
badge=lambda x: x.get_vminterfaces().count(),

netbox/netbox/views/generic/object_views.py

+3
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,12 @@ def get(self, request, *args, **kwargs):
143143
return render(request, self.get_template_name(), {
144144
'object': instance,
145145
'child_model': self.child_model,
146+
'base_template': f'{instance._meta.app_label}/{instance._meta.model_name}.html',
146147
'table': table,
148+
'table_config': f'{table.name}_config',
147149
'actions': actions,
148150
'tab': self.tab,
151+
'return_url': request.get_full_path(),
149152
**self.get_extra_context(request, instance),
150153
})
151154

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{% extends 'generic/object_children.html' %}
2+
{% load helpers %}
3+
4+
{% block bulk_edit_controls %}
5+
{{ block.super }}
6+
{% with bulk_rename_view=child_model|validated_viewname:"bulk_rename" %}
7+
{% if 'bulk_rename' in actions and bulk_rename_view %}
8+
<button type="submit" name="_rename"
9+
formaction="{% url bulk_rename_view %}?return_url={{ return_url }}"
10+
class="btn btn-outline-warning btn-sm">
11+
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
12+
</button>
13+
{% endif %}
14+
{% endwith %}
15+
{% endblock bulk_edit_controls %}
+21-51
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,27 @@
1-
{% extends 'dcim/device/base.html' %}
2-
{% load render_table from django_tables2 %}
1+
{% extends 'dcim/device/components_base.html' %}
32
{% load helpers %}
4-
{% load static %}
53

6-
{% block content %}
7-
{% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsolePortTable_config" %}
8-
9-
<form method="post">
10-
{% csrf_token %}
11-
12-
<div class="card">
13-
<div class="card-body htmx-container table-responsive" id="object_list">
14-
{% include 'htmx/table.html' %}
15-
</div>
16-
</div>
17-
18-
<div class="noprint bulk-buttons">
19-
<div class="bulk-button-group">
20-
{% if 'bulk_edit' in actions %}
21-
<div class="btn-group" role="group">
22-
<button type="submit" name="_edit" formaction="{% url 'dcim:consoleport_bulk_edit' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleports' pk=object.pk %}" class="btn btn-warning btn-sm">
23-
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
4+
{% block bulk_delete_controls %}
5+
{{ block.super }}
6+
{% with bulk_disconnect_view=child_model|validated_viewname:"bulk_disconnect" %}
7+
{% if 'bulk_disconnect' in actions and bulk_disconnect_view %}
8+
<button type="submit" name="_disconnect"
9+
formaction="{% url bulk_disconnect_view %}?return_url={{ return_url }}"
10+
class="btn btn-outline-danger btn-sm">
11+
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
2412
</button>
25-
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleport_bulk_rename' %}?return_url={% url 'dcim:device_consoleports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
26-
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
27-
</button>
28-
</div>
2913
{% endif %}
30-
<div class="btn-group" role="group">
31-
{% if 'bulk_delete' in actions %}
32-
<button type="submit" name="_delete" formaction="{% url 'dcim:consoleport_bulk_delete' %}?return_url={% url 'dcim:device_consoleports' pk=object.pk %}" class="btn btn-danger btn-sm">
33-
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
34-
</button>
35-
{% endif %}
36-
{% if 'bulk_edit' in actions %}
37-
<button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleport_bulk_disconnect' %}?return_url={% url 'dcim:device_consoleports' pk=object.pk %}" class="btn btn-outline-danger btn-sm">
38-
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
39-
</button>
40-
{% endif %}
41-
</div>
42-
</div>
43-
{% if perms.dcim.add_consoleport %}
14+
{% endwith %}
15+
{% endblock bulk_delete_controls %}
16+
17+
{% block bulk_extra_controls %}
18+
{{ block.super }}
19+
{% if perms.dcim.add_consoleport %}
4420
<div class="bulk-button-group">
45-
<a href="{% url 'dcim:consoleport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleports' pk=object.pk %}" class="btn btn-sm btn-primary">
46-
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add Console Port
47-
</a>
21+
<a href="{% url 'dcim:consoleport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleports' pk=object.pk %}"
22+
class="btn btn-primary btn-sm">
23+
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add Console Ports
24+
</a>
4825
</div>
49-
{% endif %}
50-
</div>
51-
</form>
52-
{% endblock %}
53-
54-
{% block modals %}
55-
{{ block.super }}
56-
{% table_config_form table %}
57-
{% endblock modals %}
26+
{% endif %}
27+
{% endblock bulk_extra_controls %}
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,27 @@
1-
{% extends 'dcim/device/base.html' %}
2-
{% load render_table from django_tables2 %}
1+
{% extends 'dcim/device/components_base.html' %}
32
{% load helpers %}
4-
{% load static %}
53

6-
{% block content %}
7-
{% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsoleServerPortTable_config" %}
8-
9-
<form method="post">
10-
{% csrf_token %}
11-
12-
<div class="card">
13-
<div class="card-body htmx-container table-responsive" id="object_list">
14-
{% include 'htmx/table.html' %}
15-
</div>
16-
</div>
17-
18-
<div class="noprint bulk-buttons">
19-
<div class="bulk-button-group">
20-
{% if 'bulk_edit' in actions %}
21-
<div class="btn-group" role="group">
22-
<button type="submit" name="_edit" formaction="{% url 'dcim:consoleserverport_bulk_edit' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}" class="btn btn-warning btn-sm">
23-
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
4+
{% block bulk_delete_controls %}
5+
{{ block.super }}
6+
{% with bulk_disconnect_view=child_model|validated_viewname:"bulk_disconnect" %}
7+
{% if 'bulk_disconnect' in actions and bulk_disconnect_view %}
8+
<button type="submit" name="_disconnect"
9+
formaction="{% url bulk_disconnect_view %}?return_url={{ return_url }}"
10+
class="btn btn-outline-danger btn-sm">
11+
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
2412
</button>
25-
<button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
26-
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
27-
</button>
28-
</div>
2913
{% endif %}
30-
<div class="btn-group" role="group">
31-
{% if 'bulk_delete' in actions %}
32-
<button type="submit" formaction="{% url 'dcim:consoleserverport_bulk_delete' %}?return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}" class="btn btn-danger btn-sm">
33-
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
34-
</button>
35-
{% endif %}
36-
{% if 'bulk_edit' in actions %}
37-
<button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleserverport_bulk_disconnect' %}?return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}" class="btn btn-outline-danger btn-sm">
38-
<span class="mdi mdi-ethernet-cable-off" aria-hidden="true"></span> Disconnect
39-
</button>
40-
{% endif %}
41-
</div>
42-
</div>
43-
{% if perms.dcim.add_consoleserverport %}
14+
{% endwith %}
15+
{% endblock bulk_delete_controls %}
16+
17+
{% block bulk_extra_controls %}
18+
{{ block.super }}
19+
{% if perms.dcim.add_consoleserverport %}
4420
<div class="bulk-button-group">
45-
<a href="{% url 'dcim:consoleserverport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}" class="btn btn-primary btn-sm">
46-
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add Console Server Ports
47-
</a>
21+
<a href="{% url 'dcim:consoleserverport_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}"
22+
class="btn btn-primary btn-sm">
23+
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add Console Server Ports
24+
</a>
4825
</div>
49-
{% endif %}
50-
</div>
51-
</form>
52-
{% endblock %}
53-
54-
{% block modals %}
55-
{{ block.super }}
56-
{% table_config_form table %}
57-
{% endblock modals %}
26+
{% endif %}
27+
{% endblock bulk_extra_controls %}
+10-47
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,13 @@
1-
{% extends 'dcim/device/base.html' %}
2-
{% load render_table from django_tables2 %}
3-
{% load helpers %}
4-
{% load static %}
1+
{% extends 'dcim/device/components_base.html' %}
52

6-
{% block content %}
7-
{% include 'inc/table_controls_htmx.html' with table_modal="DeviceDeviceBayTable_config" %}
8-
9-
<form method="post">
10-
{% csrf_token %}
11-
12-
<div class="card">
13-
<div class="card-body htmx-container table-responsive" id="object_list">
14-
{% include 'htmx/table.html' %}
15-
</div>
16-
</div>
17-
18-
<div class="noprint bulk-buttons">
19-
<div class="bulk-button-group">
20-
{% if 'bulk_edit' in actions %}
21-
<div class="btn-group" role="group">
22-
<button type="submit" name="_edit" formaction="{% url 'dcim:devicebay_bulk_edit' %}?device={{ object.pk }}&return_url={% url 'dcim:device_devicebays' pk=object.pk %}" class="btn btn-warning btn-sm">
23-
<i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
24-
</button>
25-
<button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={% url 'dcim:device_devicebays' pk=object.pk %}" class="btn btn-outline-warning btn-sm">
26-
<i class="mdi mdi-pencil-outline" aria-hidden="true"></i> Rename
27-
</button>
28-
</div>
29-
{% endif %}
30-
{% if 'bulk_delete' in actions %}
31-
<button type="submit" name="_delete" formaction="{% url 'dcim:devicebay_bulk_delete' %}?return_url={% url 'dcim:device_devicebays' pk=object.pk %}" class="btn btn-danger btn-sm">
32-
<i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
33-
</button>
34-
{% endif %}
35-
</div>
36-
{% if perms.dcim.add_devicebay %}
3+
{% block bulk_extra_controls %}
4+
{{ block.super }}
5+
{% if perms.dcim.add_devicebay %}
376
<div class="bulk-button-group">
38-
<a href="{% url 'dcim:devicebay_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_devicebays' pk=object.pk %}" class="btn btn-primary btn-sm">
39-
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add Device Bays
40-
</a>
7+
<a href="{% url 'dcim:devicebay_add' %}?device={{ object.pk }}&return_url={% url 'dcim:device_devicebays' pk=object.pk %}"
8+
class="btn btn-primary btn-sm">
9+
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add Device Bays
10+
</a>
4111
</div>
42-
{% endif %}
43-
</div>
44-
</form>
45-
{% endblock %}
46-
47-
{% block modals %}
48-
{{ block.super }}
49-
{% table_config_form table %}
50-
{% endblock modals %}
12+
{% endif %}
13+
{% endblock bulk_extra_controls %}

0 commit comments

Comments
 (0)