mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-14 15:52:18 -06:00
Merge pull request #20737 from netbox-community/20204-template-components
Some checks failed
CI / build (20.x, 3.12) (push) Has been cancelled
CI / build (20.x, 3.13) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Has been cancelled
Some checks failed
CI / build (20.x, 3.12) (push) Has been cancelled
CI / build (20.x, 3.13) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Has been cancelled
Closes #20204: Introduce modular template components
This commit is contained in:
0
netbox/dcim/ui/__init__.py
Normal file
0
netbox/dcim/ui/__init__.py
Normal file
189
netbox/dcim/ui/panels.py
Normal file
189
netbox/dcim/ui/panels.py
Normal file
@@ -0,0 +1,189 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from netbox.ui import attrs, panels
|
||||
|
||||
|
||||
class SitePanel(panels.ObjectAttributesPanel):
|
||||
region = attrs.NestedObjectAttr('region', linkify=True)
|
||||
group = attrs.NestedObjectAttr('group', linkify=True)
|
||||
name = attrs.TextAttr('name')
|
||||
status = attrs.ChoiceAttr('status')
|
||||
tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
|
||||
facility = attrs.TextAttr('facility')
|
||||
description = attrs.TextAttr('description')
|
||||
timezone = attrs.TimezoneAttr('time_zone')
|
||||
physical_address = attrs.AddressAttr('physical_address', map_url=True)
|
||||
shipping_address = attrs.AddressAttr('shipping_address', map_url=True)
|
||||
gps_coordinates = attrs.GPSCoordinatesAttr()
|
||||
|
||||
|
||||
class LocationPanel(panels.NestedGroupObjectPanel):
|
||||
site = attrs.RelatedObjectAttr('site', linkify=True, grouped_by='group')
|
||||
status = attrs.ChoiceAttr('status')
|
||||
tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
|
||||
facility = attrs.TextAttr('facility')
|
||||
|
||||
|
||||
class RackDimensionsPanel(panels.ObjectAttributesPanel):
|
||||
form_factor = attrs.ChoiceAttr('form_factor')
|
||||
width = attrs.ChoiceAttr('width')
|
||||
height = attrs.TextAttr('u_height', format_string='{}U', label=_('Height'))
|
||||
outer_width = attrs.NumericAttr('outer_width', unit_accessor='get_outer_unit_display')
|
||||
outer_height = attrs.NumericAttr('outer_height', unit_accessor='get_outer_unit_display')
|
||||
outer_depth = attrs.NumericAttr('outer_depth', unit_accessor='get_outer_unit_display')
|
||||
mounting_depth = attrs.TextAttr('mounting_depth', format_string='{}mm')
|
||||
|
||||
|
||||
class RackNumberingPanel(panels.ObjectAttributesPanel):
|
||||
starting_unit = attrs.TextAttr('starting_unit')
|
||||
desc_units = attrs.BooleanAttr('desc_units', label=_('Descending units'))
|
||||
|
||||
|
||||
class RackPanel(panels.ObjectAttributesPanel):
|
||||
region = attrs.NestedObjectAttr('site.region', linkify=True)
|
||||
site = attrs.RelatedObjectAttr('site', linkify=True, grouped_by='group')
|
||||
location = attrs.NestedObjectAttr('location', linkify=True)
|
||||
name = attrs.TextAttr('name')
|
||||
facility = attrs.TextAttr('facility', label=_('Facility ID'))
|
||||
tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
|
||||
status = attrs.ChoiceAttr('status')
|
||||
rack_type = attrs.RelatedObjectAttr('rack_type', linkify=True, grouped_by='manufacturer')
|
||||
role = attrs.RelatedObjectAttr('role', linkify=True)
|
||||
description = attrs.TextAttr('description')
|
||||
serial = attrs.TextAttr('serial', label=_('Serial number'), style='font-monospace', copy_button=True)
|
||||
asset_tag = attrs.TextAttr('asset_tag', style='font-monospace', copy_button=True)
|
||||
airflow = attrs.ChoiceAttr('airflow')
|
||||
space_utilization = attrs.UtilizationAttr('get_utilization')
|
||||
power_utilization = attrs.UtilizationAttr('get_power_utilization')
|
||||
|
||||
|
||||
class RackWeightPanel(panels.ObjectAttributesPanel):
|
||||
weight = attrs.NumericAttr('weight', unit_accessor='get_weight_unit_display')
|
||||
max_weight = attrs.NumericAttr('max_weight', unit_accessor='get_weight_unit_display', label=_('Maximum weight'))
|
||||
total_weight = attrs.TemplatedAttr('total_weight', template_name='dcim/rack/attrs/total_weight.html')
|
||||
|
||||
|
||||
class RackRolePanel(panels.OrganizationalObjectPanel):
|
||||
color = attrs.ColorAttr('color')
|
||||
|
||||
|
||||
class RackReservationPanel(panels.ObjectAttributesPanel):
|
||||
units = attrs.TextAttr('unit_list')
|
||||
status = attrs.ChoiceAttr('status')
|
||||
tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
|
||||
user = attrs.RelatedObjectAttr('user')
|
||||
description = attrs.TextAttr('description')
|
||||
|
||||
|
||||
class RackTypePanel(panels.ObjectAttributesPanel):
|
||||
manufacturer = attrs.RelatedObjectAttr('manufacturer', linkify=True)
|
||||
model = attrs.TextAttr('model')
|
||||
description = attrs.TextAttr('description')
|
||||
|
||||
|
||||
class DevicePanel(panels.ObjectAttributesPanel):
|
||||
region = attrs.NestedObjectAttr('site.region', linkify=True)
|
||||
site = attrs.RelatedObjectAttr('site', linkify=True, grouped_by='group')
|
||||
location = attrs.NestedObjectAttr('location', linkify=True)
|
||||
rack = attrs.TemplatedAttr('rack', template_name='dcim/device/attrs/rack.html')
|
||||
virtual_chassis = attrs.RelatedObjectAttr('virtual_chassis', linkify=True)
|
||||
parent_device = attrs.TemplatedAttr('parent_bay', template_name='dcim/device/attrs/parent_device.html')
|
||||
gps_coordinates = attrs.GPSCoordinatesAttr()
|
||||
tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
|
||||
device_type = attrs.RelatedObjectAttr('device_type', linkify=True, grouped_by='manufacturer')
|
||||
description = attrs.TextAttr('description')
|
||||
airflow = attrs.ChoiceAttr('airflow')
|
||||
serial = attrs.TextAttr('serial', label=_('Serial number'), style='font-monospace', copy_button=True)
|
||||
asset_tag = attrs.TextAttr('asset_tag', style='font-monospace', copy_button=True)
|
||||
config_template = attrs.RelatedObjectAttr('config_template', linkify=True)
|
||||
|
||||
|
||||
class DeviceManagementPanel(panels.ObjectAttributesPanel):
|
||||
title = _('Management')
|
||||
|
||||
status = attrs.ChoiceAttr('status')
|
||||
role = attrs.NestedObjectAttr('role', linkify=True, max_depth=3)
|
||||
platform = attrs.NestedObjectAttr('platform', linkify=True, max_depth=3)
|
||||
primary_ip4 = attrs.TemplatedAttr(
|
||||
'primary_ip4',
|
||||
label=_('Primary IPv4'),
|
||||
template_name='dcim/device/attrs/ipaddress.html',
|
||||
)
|
||||
primary_ip6 = attrs.TemplatedAttr(
|
||||
'primary_ip6',
|
||||
label=_('Primary IPv6'),
|
||||
template_name='dcim/device/attrs/ipaddress.html',
|
||||
)
|
||||
oob_ip = attrs.TemplatedAttr(
|
||||
'oob_ip',
|
||||
label=_('Out-of-band IP'),
|
||||
template_name='dcim/device/attrs/ipaddress.html',
|
||||
)
|
||||
cluster = attrs.RelatedObjectAttr('cluster', linkify=True)
|
||||
|
||||
|
||||
class DeviceDimensionsPanel(panels.ObjectAttributesPanel):
|
||||
title = _('Dimensions')
|
||||
|
||||
height = attrs.TextAttr('device_type.u_height', format_string='{}U')
|
||||
total_weight = attrs.TemplatedAttr('total_weight', template_name='dcim/device/attrs/total_weight.html')
|
||||
|
||||
|
||||
class DeviceTypePanel(panels.ObjectAttributesPanel):
|
||||
manufacturer = attrs.RelatedObjectAttr('manufacturer', linkify=True)
|
||||
model = attrs.TextAttr('model')
|
||||
part_number = attrs.TextAttr('part_number')
|
||||
default_platform = attrs.RelatedObjectAttr('default_platform', linkify=True)
|
||||
description = attrs.TextAttr('description')
|
||||
height = attrs.TextAttr('u_height', format_string='{}U', label=_('Height'))
|
||||
exclude_from_utilization = attrs.BooleanAttr('exclude_from_utilization')
|
||||
full_depth = attrs.BooleanAttr('is_full_depth')
|
||||
weight = attrs.NumericAttr('weight', unit_accessor='get_weight_unit_display')
|
||||
subdevice_role = attrs.ChoiceAttr('subdevice_role', label=_('Parent/child'))
|
||||
airflow = attrs.ChoiceAttr('airflow')
|
||||
front_image = attrs.ImageAttr('front_image')
|
||||
rear_image = attrs.ImageAttr('rear_image')
|
||||
|
||||
|
||||
class ModuleTypeProfilePanel(panels.ObjectAttributesPanel):
|
||||
name = attrs.TextAttr('name')
|
||||
description = attrs.TextAttr('description')
|
||||
|
||||
|
||||
class VirtualChassisMembersPanel(panels.ObjectPanel):
|
||||
"""
|
||||
A panel which lists all members of a virtual chassis.
|
||||
"""
|
||||
template_name = 'dcim/panels/virtual_chassis_members.html'
|
||||
title = _('Virtual Chassis Members')
|
||||
|
||||
def get_context(self, context):
|
||||
return {
|
||||
**super().get_context(context),
|
||||
'vc_members': context.get('vc_members'),
|
||||
}
|
||||
|
||||
def render(self, context):
|
||||
if not context.get('vc_members'):
|
||||
return ''
|
||||
return super().render(context)
|
||||
|
||||
|
||||
class PowerUtilizationPanel(panels.ObjectPanel):
|
||||
"""
|
||||
A panel which displays the power utilization statistics for a device.
|
||||
"""
|
||||
template_name = 'dcim/panels/power_utilization.html'
|
||||
title = _('Power Utilization')
|
||||
|
||||
def get_context(self, context):
|
||||
return {
|
||||
**super().get_context(context),
|
||||
'vc_members': context.get('vc_members'),
|
||||
}
|
||||
|
||||
def render(self, context):
|
||||
obj = context['object']
|
||||
if not obj.powerports.exists() or not obj.poweroutlets.exists():
|
||||
return ''
|
||||
return super().render(context)
|
||||
@@ -1,3 +1,4 @@
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.paginator import EmptyPage, PageNotAnInteger
|
||||
@@ -12,10 +13,17 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import View
|
||||
|
||||
from circuits.models import Circuit, CircuitTermination
|
||||
from dcim.ui import panels
|
||||
from extras.ui.panels import CustomFieldsPanel, ImageAttachmentsPanel, TagsPanel
|
||||
from extras.views import ObjectConfigContextView, ObjectRenderConfigView
|
||||
from ipam.models import ASN, IPAddress, Prefix, VLANGroup, VLAN
|
||||
from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
|
||||
from netbox.object_actions import *
|
||||
from netbox.ui import actions, layout
|
||||
from netbox.ui.panels import (
|
||||
CommentsPanel, JSONPanel, NestedGroupObjectPanel, ObjectsTablePanel, OrganizationalObjectPanel, RelatedObjectsPanel,
|
||||
TemplatePanel,
|
||||
)
|
||||
from netbox.views import generic
|
||||
from utilities.forms import ConfirmationForm
|
||||
from utilities.paginator import EnhancedPaginator, get_paginate_count
|
||||
@@ -221,6 +229,27 @@ class RegionListView(generic.ObjectListView):
|
||||
@register_model_view(Region)
|
||||
class RegionView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = Region.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
NestedGroupObjectPanel(),
|
||||
TagsPanel(),
|
||||
CustomFieldsPanel(),
|
||||
CommentsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
RelatedObjectsPanel(),
|
||||
],
|
||||
bottom_panels=[
|
||||
ObjectsTablePanel(
|
||||
model='dcim.Region',
|
||||
title=_('Child Regions'),
|
||||
filters={'parent_id': lambda ctx: ctx['object'].pk},
|
||||
actions=[
|
||||
actions.AddObject('dcim.Region', url_params={'parent': lambda ctx: ctx['object'].pk}),
|
||||
],
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
regions = instance.get_descendants(include_self=True)
|
||||
@@ -332,6 +361,27 @@ class SiteGroupListView(generic.ObjectListView):
|
||||
@register_model_view(SiteGroup)
|
||||
class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = SiteGroup.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
NestedGroupObjectPanel(),
|
||||
TagsPanel(),
|
||||
CustomFieldsPanel(),
|
||||
CommentsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
RelatedObjectsPanel(),
|
||||
],
|
||||
bottom_panels=[
|
||||
ObjectsTablePanel(
|
||||
model='dcim.SiteGroup',
|
||||
title=_('Child Groups'),
|
||||
filters={'parent_id': lambda ctx: ctx['object'].pk},
|
||||
actions=[
|
||||
actions.AddObject('dcim.Region', url_params={'parent': lambda ctx: ctx['object'].pk}),
|
||||
],
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
groups = instance.get_descendants(include_self=True)
|
||||
@@ -461,6 +511,39 @@ class SiteListView(generic.ObjectListView):
|
||||
@register_model_view(Site)
|
||||
class SiteView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = Site.objects.prefetch_related('tenant__group')
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.SitePanel(),
|
||||
CustomFieldsPanel(),
|
||||
TagsPanel(),
|
||||
CommentsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
RelatedObjectsPanel(),
|
||||
ImageAttachmentsPanel(),
|
||||
],
|
||||
bottom_panels=[
|
||||
ObjectsTablePanel(
|
||||
model='dcim.Location',
|
||||
filters={'site_id': lambda ctx: ctx['object'].pk},
|
||||
actions=[
|
||||
actions.AddObject('dcim.Location', url_params={'site': lambda ctx: ctx['object'].pk}),
|
||||
],
|
||||
),
|
||||
ObjectsTablePanel(
|
||||
model='dcim.Device',
|
||||
title=_('Non-Racked Devices'),
|
||||
filters={
|
||||
'site_id': lambda ctx: ctx['object'].pk,
|
||||
'rack_id': settings.FILTERS_NULL_CHOICE_VALUE,
|
||||
'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE,
|
||||
},
|
||||
actions=[
|
||||
actions.AddObject('dcim.Device', url_params={'site': lambda ctx: ctx['object'].pk}),
|
||||
],
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
@@ -561,6 +644,52 @@ class LocationListView(generic.ObjectListView):
|
||||
@register_model_view(Location)
|
||||
class LocationView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = Location.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.LocationPanel(),
|
||||
TagsPanel(),
|
||||
CustomFieldsPanel(),
|
||||
CommentsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
RelatedObjectsPanel(),
|
||||
ImageAttachmentsPanel(),
|
||||
],
|
||||
bottom_panels=[
|
||||
ObjectsTablePanel(
|
||||
model='dcim.Location',
|
||||
title=_('Child Locations'),
|
||||
filters={'parent_id': lambda ctx: ctx['object'].pk},
|
||||
actions=[
|
||||
actions.AddObject(
|
||||
'dcim.Location',
|
||||
url_params={
|
||||
'site': lambda ctx: ctx['object'].site_id,
|
||||
'parent': lambda ctx: ctx['object'].pk,
|
||||
}
|
||||
),
|
||||
],
|
||||
),
|
||||
ObjectsTablePanel(
|
||||
model='dcim.Device',
|
||||
title=_('Non-Racked Devices'),
|
||||
filters={
|
||||
'location_id': lambda ctx: ctx['object'].pk,
|
||||
'rack_id': settings.FILTERS_NULL_CHOICE_VALUE,
|
||||
'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE,
|
||||
},
|
||||
actions=[
|
||||
actions.AddObject(
|
||||
'dcim.Device',
|
||||
url_params={
|
||||
'site': lambda ctx: ctx['object'].site_id,
|
||||
'parent': lambda ctx: ctx['object'].pk,
|
||||
}
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
locations = instance.get_descendants(include_self=True)
|
||||
@@ -661,6 +790,16 @@ class RackRoleListView(generic.ObjectListView):
|
||||
@register_model_view(RackRole)
|
||||
class RackRoleView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = RackRole.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.RackRolePanel(),
|
||||
TagsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
RelatedObjectsPanel(),
|
||||
CustomFieldsPanel(),
|
||||
],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
@@ -727,7 +866,22 @@ class RackTypeListView(generic.ObjectListView):
|
||||
|
||||
@register_model_view(RackType)
|
||||
class RackTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
template_name = 'generic/object.html'
|
||||
queryset = RackType.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.RackTypePanel(),
|
||||
panels.RackDimensionsPanel(title=_('Dimensions')),
|
||||
TagsPanel(),
|
||||
CommentsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
panels.RackNumberingPanel(title=_('Numbering')),
|
||||
panels.RackWeightPanel(title=_('Weight'), exclude=['total_weight']),
|
||||
CustomFieldsPanel(),
|
||||
RelatedObjectsPanel(),
|
||||
],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
@@ -845,6 +999,22 @@ class RackElevationListView(generic.ObjectListView):
|
||||
@register_model_view(Rack)
|
||||
class RackView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.RackPanel(),
|
||||
panels.RackDimensionsPanel(title=_('Dimensions')),
|
||||
panels.RackNumberingPanel(title=_('Numbering')),
|
||||
panels.RackWeightPanel(title=_('Weight')),
|
||||
CustomFieldsPanel(),
|
||||
TagsPanel(),
|
||||
CommentsPanel(),
|
||||
ImageAttachmentsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
TemplatePanel('dcim/panels/rack_elevations.html'),
|
||||
RelatedObjectsPanel(),
|
||||
],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
|
||||
@@ -976,6 +1146,19 @@ class RackReservationListView(generic.ObjectListView):
|
||||
@register_model_view(RackReservation)
|
||||
class RackReservationView(generic.ObjectView):
|
||||
queryset = RackReservation.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.RackPanel(accessor='object.rack', only=['region', 'site', 'location', 'name']),
|
||||
panels.RackReservationPanel(title=_('Reservation')),
|
||||
CustomFieldsPanel(),
|
||||
TagsPanel(),
|
||||
CommentsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
TemplatePanel(template_name='dcim/panels/rack_reservation_elevations.html'),
|
||||
RelatedObjectsPanel(),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@register_model_view(RackReservation, 'add', detail=False)
|
||||
@@ -1049,6 +1232,10 @@ class ManufacturerListView(generic.ObjectListView):
|
||||
@register_model_view(Manufacturer)
|
||||
class ManufacturerView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = Manufacturer.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[OrganizationalObjectPanel(), TagsPanel()],
|
||||
right_panels=[RelatedObjectsPanel(), CustomFieldsPanel()],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
@@ -1122,6 +1309,18 @@ class DeviceTypeListView(generic.ObjectListView):
|
||||
@register_model_view(DeviceType)
|
||||
class DeviceTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = DeviceType.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.DeviceTypePanel(),
|
||||
TagsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
RelatedObjectsPanel(),
|
||||
CustomFieldsPanel(),
|
||||
CommentsPanel(),
|
||||
ImageAttachmentsPanel(),
|
||||
],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
@@ -1372,7 +1571,36 @@ class ModuleTypeProfileListView(generic.ObjectListView):
|
||||
|
||||
@register_model_view(ModuleTypeProfile)
|
||||
class ModuleTypeProfileView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
template_name = 'generic/object.html'
|
||||
queryset = ModuleTypeProfile.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.ModuleTypeProfilePanel(),
|
||||
TagsPanel(),
|
||||
CommentsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
JSONPanel(field_name='schema', title=_('Schema')),
|
||||
CustomFieldsPanel(),
|
||||
],
|
||||
bottom_panels=[
|
||||
ObjectsTablePanel(
|
||||
model='dcim.ModuleType',
|
||||
title=_('Module Types'),
|
||||
filters={
|
||||
'profile_id': lambda ctx: ctx['object'].pk,
|
||||
},
|
||||
actions=[
|
||||
actions.AddObject(
|
||||
'dcim.ModuleType',
|
||||
url_params={
|
||||
'profile': lambda ctx: ctx['object'].pk,
|
||||
}
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@register_model_view(ModuleTypeProfile, 'add', detail=False)
|
||||
@@ -2213,6 +2441,43 @@ class DeviceListView(generic.ObjectListView):
|
||||
@register_model_view(Device)
|
||||
class DeviceView(generic.ObjectView):
|
||||
queryset = Device.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.DevicePanel(),
|
||||
panels.VirtualChassisMembersPanel(),
|
||||
CustomFieldsPanel(),
|
||||
TagsPanel(),
|
||||
CommentsPanel(),
|
||||
ObjectsTablePanel(
|
||||
model='dcim.VirtualDeviceContext',
|
||||
filters={'device_id': lambda ctx: ctx['object'].pk},
|
||||
actions=[
|
||||
actions.AddObject('dcim.VirtualDeviceContext', url_params={'device': lambda ctx: ctx['object'].pk}),
|
||||
],
|
||||
),
|
||||
],
|
||||
right_panels=[
|
||||
panels.DeviceManagementPanel(),
|
||||
panels.PowerUtilizationPanel(),
|
||||
ObjectsTablePanel(
|
||||
model='ipam.Service',
|
||||
title=_('Application Services'),
|
||||
filters={'device_id': lambda ctx: ctx['object'].pk},
|
||||
actions=[
|
||||
actions.AddObject(
|
||||
'ipam.Service',
|
||||
url_params={
|
||||
'parent_object_type': lambda ctx: ContentType.objects.get_for_model(ctx['object']).pk,
|
||||
'parent': lambda ctx: ctx['object'].pk
|
||||
}
|
||||
),
|
||||
],
|
||||
),
|
||||
ImageAttachmentsPanel(),
|
||||
panels.DeviceDimensionsPanel(),
|
||||
TemplatePanel('dcim/panels/device_rack_elevations.html'),
|
||||
],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
# VirtualChassis members
|
||||
@@ -2225,7 +2490,7 @@ class DeviceView(generic.ObjectView):
|
||||
|
||||
return {
|
||||
'vc_members': vc_members,
|
||||
'svg_extra': f'highlight=id:{instance.pk}'
|
||||
'svg_extra': f'highlight=id:{instance.pk}',
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user