Replace EmbeddedTablePanel with ObjectsTablePanel

This commit is contained in:
Jeremy Stretch 2025-11-03 10:30:13 -05:00
parent 37bea1e98e
commit c392988212
5 changed files with 58 additions and 41 deletions

View File

@ -18,9 +18,9 @@ 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 layout
from netbox.ui import actions, layout
from netbox.ui.panels import (
CommentsPanel, CustomFieldsPanel, EmbeddedTablePanel, ImageAttachmentsPanel, PluginContentPanel,
CommentsPanel, CustomFieldsPanel, ImageAttachmentsPanel, ObjectsTablePanel, PluginContentPanel,
RelatedObjectsPanel, TagsPanel,
)
from netbox.views import generic
@ -485,19 +485,24 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView):
),
layout.Row(
layout.Column(
EmbeddedTablePanel(
'dcim:location_list',
url_params={'site_id': lambda x: x.pk},
title=_('Locations')
ObjectsTablePanel(
model='dcim.Location',
filters={'site_id': lambda obj: obj.pk},
actions=[
actions.AddObject('dcim.Location', url_params={'site': lambda obj: obj.pk}),
],
),
EmbeddedTablePanel(
'dcim:device_list',
url_params={
'site_id': lambda x: x.pk,
ObjectsTablePanel(
model='dcim.Device',
title=_('Non-Racked Devices'),
filters={
'site_id': lambda obj: obj.pk,
'rack_id': settings.FILTERS_NULL_CHOICE_VALUE,
'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE,
},
title=_('Non-Racked Devices')
actions=[
actions.AddObject('dcim.Device', url_params={'site': lambda obj: obj.pk}),
],
),
PluginContentPanel('full_width_page'),
),

View File

@ -32,6 +32,8 @@ class PanelAction:
url_params = {
k: v(obj) if callable(v) else v for k, v in self.url_params.items()
}
if 'return_url' not in url_params:
url_params['return_url'] = obj.get_absolute_url()
url = f'{url}?{urlencode(url_params)}'
return url
@ -49,8 +51,11 @@ class AddObject(PanelAction):
button_icon = 'plus-thick'
def __init__(self, model, label=None, url_params=None):
# Resolve the model class from its app.name label
app_label, model_name = model.split('.')
model = apps.get_model(app_label, model_name)
view_name = get_viewname(model, 'add')
super().__init__(view_name=view_name, label=label, url_params=url_params)
# Require "add" permission on the model by default
self.permissions = [get_permission_for_model(model, 'add')]

View File

@ -1,5 +1,6 @@
from abc import ABC, ABCMeta
from django.apps import apps
from django.contrib.contenttypes.models import ContentType
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
@ -9,14 +10,15 @@ from netbox.ui.attrs import Attr
from utilities.querydict import dict_to_querydict
from utilities.string import title
from utilities.templatetags.plugins import _get_registered_content
from utilities.views import get_viewname
__all__ = (
'CommentsPanel',
'CustomFieldsPanel',
'EmbeddedTablePanel',
'ImageAttachmentsPanel',
'NestedGroupObjectPanel',
'ObjectPanel',
'ObjectsTablePanel',
'RelatedObjectsPanel',
'Panel',
'PluginContentPanel',
@ -130,9 +132,33 @@ class RelatedObjectsPanel(Panel):
})
class ImageAttachmentsPanel(Panel):
template_name = 'ui/panels/image_attachments.html'
title = _('Image Attachments')
class ObjectsTablePanel(Panel):
template_name = 'ui/panels/objects_table.html'
title = None
def __init__(self, model, filters=None, **kwargs):
super().__init__(**kwargs)
# Resolve the model class from its app.name label
app_label, model_name = model.split('.')
self.model = apps.get_model(app_label, model_name)
self.filters = filters or {}
if self.title is None:
self.title = title(self.model._meta.verbose_name_plural)
def get_context(self, obj):
url_params = {
k: v(obj) if callable(v) else v for k, v in self.filters.items()
}
if 'return_url' not in url_params:
url_params['return_url'] = obj.get_absolute_url()
return {
'viewname': get_viewname(self.model, 'list'),
'url_params': dict_to_querydict(url_params),
}
class ImageAttachmentsPanel(ObjectsTablePanel):
actions = [
actions.AddObject(
'extras.imageattachment',
@ -145,25 +171,8 @@ class ImageAttachmentsPanel(Panel):
),
]
class EmbeddedTablePanel(Panel):
template_name = 'ui/panels/embedded_table.html'
title = None
def __init__(self, view_name, url_params=None, **kwargs):
super().__init__(**kwargs)
self.view_name = view_name
self.url_params = url_params or {}
def get_context(self, obj):
url_params = {
k: v(obj) if callable(v) else v for k, v in self.url_params.items()
}
# url_params['return_url'] = return_url or context['request'].path
return {
'viewname': self.view_name,
'url_params': dict_to_querydict(url_params),
}
def __init__(self, **kwargs):
super().__init__('extras.imageattachment', **kwargs)
class PluginContentPanel(Panel):

View File

@ -1,7 +0,0 @@
{% extends "ui/panels/_base.html" %}
{% load i18n %}
{# TODO: Add "attach an image" button in panel header #}
{% block panel_content %}
{% htmx_table 'extras:imageattachment_list' object_type_id=object|content_type_id object_id=object.pk %}
{% endblock panel_content %}

View File

@ -0,0 +1,5 @@
{% extends "ui/panels/_base.html" %}
{% block panel_content %}
{% include 'builtins/htmx_table.html' %}
{% endblock panel_content %}