Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion netbox/ipam/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,26 @@ def get_extra_context(self, request, instance):
)
export_targets_table.configure(request)

related_models = self.get_related_models(
request,
instance,
omit=(Interface, VMInterface),
extra=(
(
Interface.objects.restrict(request.user, 'view').filter(vrf=instance),
'vrf_id',
_('Device Interfaces')
),
(
VMInterface.objects.restrict(request.user, 'view').filter(vrf=instance),
'vrf_id',
_('VM Interfaces')
),
),
)

return {
'related_models': self.get_related_models(request, instance, omit=[Interface, VMInterface]),
'related_models': related_models,
'import_targets_table': import_targets_table,
'export_targets_table': export_targets_table,
}
Expand Down
24 changes: 12 additions & 12 deletions netbox/templates/inc/panels/related_objects.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
<div class="card">
<h2 class="card-header">{% trans "Related Objects" %}</h2>
<ul class="list-group list-group-flush" role="presentation">
{% for qs, filter_param in related_models %}
{% with viewname=qs.model|validated_viewname:"list" %}
{% for related_object_count in related_models %}
{% with viewname=related_object_count.queryset.model|validated_viewname:"list" %}
{% if viewname is not None %}
<a href="{% url viewname %}?{{ filter_param }}={{ object.pk }}" class="list-group-item list-group-item-action d-flex justify-content-between">
{{ qs.model|meta:"verbose_name_plural"|bettertitle }}
{% with count=qs.count %}
{% if count %}
<span class="badge text-bg-primary rounded-pill">{{ count }}</span>
{% else %}
<span class="badge text-bg-light rounded-pill">&mdash;</span>
{% endif %}
{% endwith %}
</a>
<a href="{% url viewname %}?{{ related_object_count.filter_param }}={{ object.pk }}" class="list-group-item list-group-item-action d-flex justify-content-between">
{{ related_object_count.name }}
{% with count=related_object_count.queryset.count %}
{% if count %}
<span class="badge text-bg-primary rounded-pill">{{ count }}</span>
{% else %}
<span class="badge text-bg-light rounded-pill">&mdash;</span>
{% endif %}
{% endwith %}
</a>
{% endif %}
{% endwith %}
{% empty %}
Expand Down
26 changes: 21 additions & 5 deletions netbox/utilities/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from dataclasses import dataclass
from typing import Iterable

from django.conf import settings
from django.contrib.auth.mixins import AccessMixin
from django.core.exceptions import ImproperlyConfigured
from django.db.models import QuerySet
from django.urls import reverse
from django.urls.exceptions import NoReverseMatch
from django.utils.translation import gettext_lazy as _
Expand All @@ -12,6 +14,7 @@
from netbox.registry import registry
from utilities.relations import get_related_models
from utilities.request import safe_for_redirect
from utilities.string import title
from .permissions import resolve_permission

__all__ = (
Expand Down Expand Up @@ -177,8 +180,17 @@ class GetRelatedModelsMixin:
"""
Provides logic for collecting all related models for the currently viewed model.
"""
@dataclass
class RelatedObjectCount:
queryset: QuerySet
filter_param: str
label: str = ''

def get_related_models(self, request, instance, omit=[], extra=[]):
@property
def name(self):
return self.label or title(_(self.queryset.model._meta.verbose_name_plural))

def get_related_models(self, request, instance, omit=None, extra=None):
"""
Get related models of the view's `queryset` model without those listed in `omit`. Will be sorted alphabetical.

Expand All @@ -191,14 +203,15 @@ def get_related_models(self, request, instance, omit=[], extra=[]):
extra: Add extra models to the list of automatically determined related models. Can be used to add indirect
relationships.
"""
omit = omit or []
model = self.queryset.model
related = filter(
lambda m: m[0] is not model and m[0] not in omit,
get_related_models(model, False)
)

related_models = [
(
self.RelatedObjectCount(
model.objects.restrict(request.user, 'view').filter(**(
{f'{field}__in': instance}
if isinstance(instance, Iterable)
Expand All @@ -208,11 +221,14 @@ def get_related_models(self, request, instance, omit=[], extra=[]):
)
for model, field in related
]
related_models.extend(extra)
if extra is not None:
related_models.extend([
self.RelatedObjectCount(*attrs) for attrs in extra
])

return sorted(
filter(lambda qs: qs[0].exists(), related_models),
key=lambda qs: qs[0].model._meta.verbose_name.lower(),
filter(lambda roc: roc.queryset.exists(), related_models),
key=lambda roc: roc.name,
)


Expand Down