Closes #21929: Eliminate redundant object view templates (#21930)

This commit is contained in:
Jeremy Stretch 2026-04-20 12:38:41 -04:00 committed by GitHub
parent b68b0c6d78
commit 1f9ed248bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 59 additions and 65 deletions

View File

@ -4060,6 +4060,7 @@ class InventoryItemRoleListView(generic.ObjectListView):
@register_model_view(InventoryItemRole)
class InventoryItemRoleView(GetRelatedModelsMixin, generic.ObjectView):
queryset = InventoryItemRole.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.InventoryItemRolePanel(),
@ -4308,6 +4309,7 @@ class CableListView(generic.ObjectListView):
@register_model_view(Cable)
class CableView(generic.ObjectView):
queryset = Cable.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.CablePanel(),
@ -4458,6 +4460,7 @@ class VirtualChassisListView(generic.ObjectListView):
@register_model_view(VirtualChassis)
class VirtualChassisView(generic.ObjectView):
queryset = VirtualChassis.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.VirtualChassisPanel(),
@ -4883,6 +4886,7 @@ class VirtualDeviceContextListView(generic.ObjectListView):
@register_model_view(VirtualDeviceContext)
class VirtualDeviceContextView(GetRelatedModelsMixin, generic.ObjectView):
queryset = VirtualDeviceContext.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.VirtualDeviceContextPanel(),
@ -4968,6 +4972,7 @@ class MACAddressListView(generic.ObjectListView):
@register_model_view(MACAddress)
class MACAddressView(generic.ObjectView):
queryset = MACAddress.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.MACAddressPanel(),

View File

@ -65,6 +65,7 @@ class CustomFieldListView(generic.ObjectListView):
@register_model_view(CustomField)
class CustomFieldView(generic.ObjectView):
queryset = CustomField.objects.select_related('choice_set')
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.CustomFieldPanel(),
@ -148,6 +149,7 @@ class CustomFieldChoiceSetListView(generic.ObjectListView):
@register_model_view(CustomFieldChoiceSet)
class CustomFieldChoiceSetView(generic.ObjectView):
queryset = CustomFieldChoiceSet.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.CustomFieldChoiceSetPanel(),
@ -231,6 +233,7 @@ class CustomLinkListView(generic.ObjectListView):
@register_model_view(CustomLink)
class CustomLinkView(generic.ObjectView):
queryset = CustomLink.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.CustomLinkPanel(),
@ -298,6 +301,7 @@ class ExportTemplateListView(generic.ObjectListView):
@register_model_view(ExportTemplate)
class ExportTemplateView(generic.ObjectView):
queryset = ExportTemplate.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.ExportTemplatePanel(),
@ -372,6 +376,7 @@ class SavedFilterListView(SharedObjectViewMixin, generic.ObjectListView):
@register_model_view(SavedFilter)
class SavedFilterView(SharedObjectViewMixin, generic.ObjectView):
queryset = SavedFilter.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.SavedFilterPanel(),
@ -443,6 +448,7 @@ class TableConfigListView(SharedObjectViewMixin, generic.ObjectListView):
@register_model_view(TableConfig)
class TableConfigView(SharedObjectViewMixin, generic.ObjectView):
queryset = TableConfig.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.TableConfigPanel(),
@ -545,6 +551,7 @@ class NotificationGroupListView(generic.ObjectListView):
@register_model_view(NotificationGroup)
class NotificationGroupView(generic.ObjectView):
queryset = NotificationGroup.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.NotificationGroupPanel(),
@ -738,6 +745,7 @@ class WebhookListView(generic.ObjectListView):
@register_model_view(Webhook)
class WebhookView(generic.ObjectView):
queryset = Webhook.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.WebhookPanel(),
@ -807,6 +815,7 @@ class EventRuleListView(generic.ObjectListView):
@register_model_view(EventRule)
class EventRuleView(generic.ObjectView):
queryset = EventRule.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.EventRulePanel(),
@ -878,6 +887,7 @@ class TagListView(generic.ObjectListView):
@register_model_view(Tag)
class TagView(generic.ObjectView):
queryset = Tag.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.TagPanel(),
@ -969,6 +979,7 @@ class ConfigContextProfileListView(generic.ObjectListView):
@register_model_view(ConfigContextProfile)
class ConfigContextProfileView(generic.ObjectView):
queryset = ConfigContextProfile.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.ConfigContextProfilePanel(),
@ -1043,6 +1054,7 @@ class ConfigContextListView(generic.ObjectListView):
@register_model_view(ConfigContext)
class ConfigContextView(generic.ObjectView):
queryset = ConfigContext.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.ConfigContextPanel(),
@ -1172,6 +1184,7 @@ class ConfigTemplateListView(generic.ObjectListView):
@register_model_view(ConfigTemplate)
class ConfigTemplateView(generic.ObjectView):
queryset = ConfigTemplate.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.ConfigTemplatePanel(),
@ -1302,6 +1315,7 @@ class ImageAttachmentListView(generic.ObjectListView):
@register_model_view(ImageAttachment)
class ImageAttachmentView(generic.ObjectView):
queryset = ImageAttachment.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.ImageAttachmentPanel(),

View File

@ -51,6 +51,7 @@ class VRFListView(generic.ObjectListView):
@register_model_view(VRF)
class VRFView(GetRelatedModelsMixin, generic.ObjectView):
queryset = VRF.objects.all()
template_name = 'generic/object.html'
layout = layout.Layout(
layout.Row(
layout.Column(
@ -172,6 +173,7 @@ class RouteTargetListView(generic.ObjectListView):
@register_model_view(RouteTarget)
class RouteTargetView(generic.ObjectView):
queryset = RouteTarget.objects.all()
template_name = 'generic/object.html'
layout = layout.Layout(
layout.Row(
layout.Column(
@ -1332,6 +1334,7 @@ class VLANTranslationPolicyListView(generic.ObjectListView):
@register_model_view(VLANTranslationPolicy)
class VLANTranslationPolicyView(generic.ObjectView):
queryset = VLANTranslationPolicy.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.VLANTranslationPolicyPanel(),
@ -1414,6 +1417,7 @@ class VLANTranslationRuleListView(generic.ObjectListView):
@register_model_view(VLANTranslationRule)
class VLANTranslationRuleView(generic.ObjectView):
queryset = VLANTranslationRule.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.VLANTranslationRulePanel(),
@ -1753,6 +1757,7 @@ class ServiceTemplateListView(generic.ObjectListView):
@register_model_view(ServiceTemplate)
class ServiceTemplateView(generic.ObjectView):
queryset = ServiceTemplate.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.ServiceTemplatePanel(),

View File

@ -17,7 +17,7 @@ from tenancy.forms import ContactAssignmentFilterForm
from tenancy.models import ContactAssignment
from tenancy.tables import ContactAssignmentTable
from utilities.permissions import get_permission_for_model
from utilities.views import ConditionalLoginRequiredMixin, GetReturnURLMixin, ViewTab
from utilities.views import ConditionalLoginRequiredMixin, GetReturnURLMixin, ViewTab, get_default_template
from .base import BaseMultiObjectView
from .object_views import ObjectChildrenView
@ -72,15 +72,10 @@ class ObjectChangeLogView(ConditionalLoginRequiredMixin, View):
)
objectchanges_table.configure(request)
# Default to using "<app>/<model>.html" as the template, if it exists. Otherwise,
# fall back to using base.html.
if self.base_template is None:
self.base_template = f"{model._meta.app_label}/{model._meta.model_name}.html"
return render(request, 'extras/object_changelog.html', {
'object': obj,
'table': objectchanges_table,
'base_template': self.base_template,
'base_template': self.base_template or get_default_template(model),
'tab': self.tab,
})
@ -107,15 +102,10 @@ class ObjectImageAttachmentsView(ConditionalLoginRequiredMixin, View):
object_id=obj.pk,
)
# Default to using "<app>/<model>.html" as the template, if it exists. Otherwise,
# fall back to using base.html.
if self.base_template is None:
self.base_template = f"{model._meta.app_label}/{model._meta.model_name}.html"
return render(request, 'extras/object_imageattachments.html', {
'object': obj,
'image_attachments': image_attachments,
'base_template': self.base_template,
'base_template': self.base_template or get_default_template(model),
'tab': self.tab,
})
@ -167,16 +157,11 @@ class ObjectJournalView(ConditionalLoginRequiredMixin, View):
else:
form = None
# Default to using "<app>/<model>.html" as the template, if it exists. Otherwise,
# fall back to using base.html.
if self.base_template is None:
self.base_template = f"{model._meta.app_label}/{model._meta.model_name}.html"
return render(request, 'extras/object_journal.html', {
'object': obj,
'form': form,
'table': journalentry_table,
'base_template': self.base_template,
'base_template': self.base_template or get_default_template(model),
'tab': self.tab,
})
@ -222,15 +207,10 @@ class ObjectJobsView(ConditionalLoginRequiredMixin, View):
jobs_table = JobTable(data=jobs, orderable=False)
jobs_table.configure(request)
# Default to using "<app>/<model>.html" as the template, if it exists. Otherwise,
# fall back to using base.html.
if self.base_template is None:
self.base_template = f"{model._meta.app_label}/{model._meta.model_name}.html"
return render(request, 'core/object_jobs.html', {
'object': obj,
'table': jobs_table,
'base_template': self.base_template,
'base_template': self.base_template or get_default_template(model),
'tab': self.tab,
})

View File

@ -22,7 +22,7 @@ from utilities.permissions import get_permission_for_model
from utilities.querydict import normalize_querydict, prepare_cloned_fields
from utilities.request import safe_for_redirect
from utilities.tables import get_table_configs
from utilities.views import GetReturnURLMixin, get_action_url
from utilities.views import GetReturnURLMixin, get_action_url, get_default_template
from .base import BaseObjectView
from .mixins import ActionsMixin, TableMixin
@ -163,7 +163,7 @@ class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin):
'object': instance,
'model': self.child_model,
'child_model': self.child_model,
'base_template': f'{instance._meta.app_label}/{instance._meta.model_name}.html',
'base_template': get_default_template(instance),
'table': table,
'table_config': f'{table.name}_config',
'table_configs': get_table_configs(table, request.user),

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -1 +0,0 @@
{% extends 'generic/object.html' %}

View File

@ -328,6 +328,7 @@ class ContactRoleListView(generic.ObjectListView):
@register_model_view(ContactRole)
class ContactRoleView(GetRelatedModelsMixin, generic.ObjectView):
queryset = ContactRole.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
OrganizationalObjectPanel(),
@ -402,6 +403,7 @@ class ContactListView(generic.ObjectListView):
@register_model_view(Contact)
class ContactView(generic.ObjectView):
queryset = Contact.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.ContactPanel(),

View File

@ -6,6 +6,8 @@ from django.contrib.auth.mixins import AccessMixin
from django.core.exceptions import ImproperlyConfigured
from django.db.models import QuerySet
from django.http import HttpResponseForbidden
from django.template import TemplateDoesNotExist
from django.template.loader import get_template
from django.urls import reverse
from django.urls.exceptions import NoReverseMatch
from django.utils.translation import gettext_lazy as _
@ -29,6 +31,7 @@ __all__ = (
'TokenConditionalLoginRequiredMixin',
'ViewTab',
'get_action_url',
'get_default_template',
'get_viewname',
'register_model_view',
)
@ -335,6 +338,19 @@ def get_action_url(model, action=None, rest_api=False, kwargs=None):
return reverse(get_viewname(model, action, rest_api), kwargs=kwargs)
def get_default_template(model):
"""
Return the base template for the given model. If the presumed "{app}/{model}.html" template
does not exist, fall back to "generic/object.html".
"""
template_name = f'{model._meta.app_label}/{model._meta.model_name}.html'
try:
get_template(template_name)
return template_name
except TemplateDoesNotExist:
return 'generic/object.html'
def register_model_view(model, name='', path=None, detail=True, kwargs=None):
"""
This decorator can be used to "attach" a view to any model in NetBox. This is typically used to inject

View File

@ -208,6 +208,7 @@ class TunnelTerminationListView(generic.ObjectListView):
@register_model_view(TunnelTermination)
class TunnelTerminationView(generic.ObjectView):
queryset = TunnelTermination.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.TunnelTerminationPanel(),
@ -278,6 +279,7 @@ class IKEProposalListView(generic.ObjectListView):
@register_model_view(IKEProposal)
class IKEProposalView(generic.ObjectView):
queryset = IKEProposal.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.IKEProposalPanel(),
@ -351,6 +353,7 @@ class IKEPolicyListView(generic.ObjectListView):
@register_model_view(IKEPolicy)
class IKEPolicyView(GetRelatedModelsMixin, generic.ObjectView):
queryset = IKEPolicy.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.IKEPolicyPanel(),
@ -430,6 +433,7 @@ class IPSecProposalListView(generic.ObjectListView):
@register_model_view(IPSecProposal)
class IPSecProposalView(generic.ObjectView):
queryset = IPSecProposal.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.IPSecProposalPanel(),
@ -503,6 +507,7 @@ class IPSecPolicyListView(generic.ObjectListView):
@register_model_view(IPSecPolicy)
class IPSecPolicyView(GetRelatedModelsMixin, generic.ObjectView):
queryset = IPSecPolicy.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.IPSecPolicyPanel(),
@ -582,6 +587,7 @@ class IPSecProfileListView(generic.ObjectListView):
@register_model_view(IPSecProfile)
class IPSecProfileView(generic.ObjectView):
queryset = IPSecProfile.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.IPSecProfilePanel(),
@ -650,6 +656,7 @@ class L2VPNListView(generic.ObjectListView):
@register_model_view(L2VPN)
class L2VPNView(generic.ObjectView):
queryset = L2VPN.objects.all()
template_name = 'generic/object.html'
layout = layout.Layout(
layout.Row(
layout.Column(
@ -764,6 +771,7 @@ class L2VPNTerminationListView(generic.ObjectListView):
@register_model_view(L2VPNTermination)
class L2VPNTerminationView(generic.ObjectView):
queryset = L2VPNTermination.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.L2VPNTerminationPanel(),

View File

@ -143,6 +143,7 @@ class WirelessLANListView(generic.ObjectListView):
@register_model_view(WirelessLAN)
class WirelessLANView(generic.ObjectView):
queryset = WirelessLAN.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.WirelessLANPanel(),
@ -218,6 +219,7 @@ class WirelessLinkListView(generic.ObjectListView):
@register_model_view(WirelessLink)
class WirelessLinkView(generic.ObjectView):
queryset = WirelessLink.objects.all()
template_name = 'generic/object.html'
layout = layout.SimpleLayout(
left_panels=[
panels.WirelessLinkInterfacePanel('interface_a', title=_('Interface A')),