mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-30 20:36:26 -06:00
#6414: Add FKs for region, site group, and location on Prefix
This commit is contained in:
parent
727de0fb59
commit
5dd92ff048
@ -2,9 +2,9 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
|
||||
from dcim.api.serializers_.sites import SiteSerializer
|
||||
from ipam.api.field_serializers import IPAddressField, IPNetworkField
|
||||
from ipam.choices import *
|
||||
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS
|
||||
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS, PREFIX_SCOPE_TYPES
|
||||
from ipam.models import Aggregate, IPAddress, IPRange, Prefix
|
||||
from netbox.api.fields import ChoiceField, ContentTypeField
|
||||
from netbox.api.serializers import NetBoxModelSerializer
|
||||
@ -15,7 +15,6 @@ from .nested import NestedIPAddressSerializer
|
||||
from .roles import RoleSerializer
|
||||
from .vlans import VLANSerializer
|
||||
from .vrfs import VRFSerializer
|
||||
from ..field_serializers import IPAddressField, IPNetworkField
|
||||
|
||||
__all__ = (
|
||||
'AggregateSerializer',
|
||||
@ -45,7 +44,16 @@ class AggregateSerializer(NetBoxModelSerializer):
|
||||
|
||||
class PrefixSerializer(NetBoxModelSerializer):
|
||||
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
|
||||
site = SiteSerializer(nested=True, required=False, allow_null=True)
|
||||
scope_type = ContentTypeField(
|
||||
queryset=ContentType.objects.filter(
|
||||
model__in=PREFIX_SCOPE_TYPES
|
||||
),
|
||||
allow_null=True,
|
||||
required=False,
|
||||
default=None
|
||||
)
|
||||
# TODO: Handle writing to scope
|
||||
scope = serializers.SerializerMethodField(read_only=True)
|
||||
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
vlan = VLANSerializer(nested=True, required=False, allow_null=True)
|
||||
@ -58,12 +66,20 @@ class PrefixSerializer(NetBoxModelSerializer):
|
||||
class Meta:
|
||||
model = Prefix
|
||||
fields = [
|
||||
'id', 'url', 'display_url', 'display', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status',
|
||||
'role', 'is_pool', 'mark_utilized', 'description', 'comments', 'tags', 'custom_fields',
|
||||
'id', 'url', 'display_url', 'display', 'family', 'prefix', 'site', 'vrf', 'scope_type', 'scope', 'tenant',
|
||||
'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description', 'comments', 'tags', 'custom_fields',
|
||||
'created', 'last_updated', 'children', '_depth',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'family', 'prefix', 'description', '_depth')
|
||||
|
||||
@extend_schema_field(serializers.JSONField(allow_null=True))
|
||||
def get_scope(self, obj):
|
||||
if obj.scope is None:
|
||||
return None
|
||||
serializer = get_serializer_for_model(obj.scope)
|
||||
context = {'request': self.context['request']}
|
||||
return serializer(obj.scope, nested=True, context=context).data
|
||||
|
||||
|
||||
class PrefixLengthSerializer(serializers.Serializer):
|
||||
|
||||
|
@ -79,7 +79,7 @@ class RoleViewSet(NetBoxModelViewSet):
|
||||
|
||||
|
||||
class PrefixViewSet(NetBoxModelViewSet):
|
||||
queryset = Prefix.objects.all()
|
||||
queryset = Prefix.objects.prefetch_related('region', 'site_group', 'site', 'location')
|
||||
serializer_class = serializers.PrefixSerializer
|
||||
filterset_class = filtersets.PrefixFilterSet
|
||||
|
||||
|
@ -23,6 +23,11 @@ VRF_RD_MAX_LENGTH = 21
|
||||
PREFIX_LENGTH_MIN = 1
|
||||
PREFIX_LENGTH_MAX = 127 # IPv6
|
||||
|
||||
# models values for ContentTypes which may be Prefix scope types
|
||||
PREFIX_SCOPE_TYPES = (
|
||||
'region', 'sitegroup', 'site', 'location',
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# IPAddresses
|
||||
|
@ -9,7 +9,7 @@ from drf_spectacular.utils import extend_schema_field
|
||||
from netaddr.core import AddrFormatError
|
||||
|
||||
from circuits.models import Provider
|
||||
from dcim.models import Device, Interface, Region, Site, SiteGroup
|
||||
from dcim.models import Device, Interface, Location, Region, Site, SiteGroup
|
||||
from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet
|
||||
from tenancy.filtersets import TenancyFilterSet
|
||||
from utilities.filters import (
|
||||
@ -332,6 +332,7 @@ class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
||||
to_field_name='rd',
|
||||
label=_('VRF (RD)'),
|
||||
)
|
||||
# TODO: Figure out region & site filters
|
||||
region_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Region.objects.all(),
|
||||
field_name='site__region',
|
||||
@ -368,6 +369,17 @@ class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
||||
to_field_name='slug',
|
||||
label=_('Site (slug)'),
|
||||
)
|
||||
location_id = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Location.objects.all(),
|
||||
lookup_expr='in',
|
||||
label=_('Location (ID)'),
|
||||
)
|
||||
location = TreeNodeMultipleChoiceFilter(
|
||||
queryset=Location.objects.all(),
|
||||
lookup_expr='in',
|
||||
to_field_name='slug',
|
||||
label=_('Location (slug)'),
|
||||
)
|
||||
vlan_id = django_filters.ModelMultipleChoiceFilter(
|
||||
queryset=VLAN.objects.all(),
|
||||
label=_('VLAN (ID)'),
|
||||
|
@ -201,12 +201,18 @@ class PrefixForm(TenancyForm, NetBoxModelForm):
|
||||
required=False,
|
||||
label=_('VRF')
|
||||
)
|
||||
site = DynamicModelChoiceField(
|
||||
label=_('Site'),
|
||||
queryset=Site.objects.all(),
|
||||
scope_type = ContentTypeChoiceField(
|
||||
queryset=ContentType.objects.filter(model__in=PREFIX_SCOPE_TYPES),
|
||||
widget=HTMXSelect(),
|
||||
required=False,
|
||||
selector=True,
|
||||
null_option='None'
|
||||
label=_('Scope type')
|
||||
)
|
||||
scope = DynamicModelChoiceField(
|
||||
label=_('Scope'),
|
||||
queryset=Site.objects.none(), # Initial queryset
|
||||
required=False,
|
||||
disabled=True,
|
||||
selector=True
|
||||
)
|
||||
vlan = DynamicModelChoiceField(
|
||||
queryset=VLAN.objects.all(),
|
||||
@ -228,7 +234,8 @@ class PrefixForm(TenancyForm, NetBoxModelForm):
|
||||
FieldSet(
|
||||
'prefix', 'status', 'vrf', 'role', 'is_pool', 'mark_utilized', 'description', 'tags', name=_('Prefix')
|
||||
),
|
||||
FieldSet('site', 'vlan', name=_('Site/VLAN Assignment')),
|
||||
FieldSet('scope_type', 'scope', name=_('Scope')),
|
||||
FieldSet('vlan', name=_('VLAN Assignment')),
|
||||
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
|
||||
)
|
||||
|
||||
@ -239,6 +246,32 @@ class PrefixForm(TenancyForm, NetBoxModelForm):
|
||||
'description', 'comments', 'tags',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instance = kwargs.get('instance')
|
||||
initial = kwargs.get('initial', {})
|
||||
|
||||
if instance is not None and instance.scope and 'scope_type' not in initial:
|
||||
initial['scope_type'] = instance.scope_type.pk
|
||||
initial['scope'] = instance.scope
|
||||
kwargs['initial'] = initial
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if scope_type := get_field_value(self, 'scope_type'):
|
||||
try:
|
||||
scope_type = ContentType.objects.get(pk=scope_type)
|
||||
model = scope_type.model_class()
|
||||
self.fields['scope'].queryset = model.objects.all()
|
||||
self.fields['scope'].widget.attrs['selector'] = model._meta.label_lower
|
||||
self.fields['scope'].disabled = False
|
||||
self.fields['scope'].label = _(bettertitle(model._meta.verbose_name))
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.instance.scope = self.cleaned_data['scope']
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class IPRangeForm(TenancyForm, NetBoxModelForm):
|
||||
vrf = DynamicModelChoiceField(
|
||||
|
@ -163,6 +163,15 @@ class PrefixType(NetBoxObjectType, BaseIPAddressFamilyType):
|
||||
vlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None
|
||||
role: Annotated["RoleType", strawberry.lazy('ipam.graphql.types')] | None
|
||||
|
||||
@strawberry_django.field
|
||||
def scope(self) -> Annotated[Union[
|
||||
Annotated["LocationType", strawberry.lazy('dcim.graphql.types')],
|
||||
Annotated["RegionType", strawberry.lazy('dcim.graphql.types')],
|
||||
Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')],
|
||||
Annotated["SiteType", strawberry.lazy('dcim.graphql.types')],
|
||||
], strawberry.union("PrefixScopeType")] | None:
|
||||
return self.scope
|
||||
|
||||
|
||||
@strawberry_django.type(
|
||||
models.RIR,
|
||||
|
28
netbox/ipam/migrations/0071_prefix_scope.py
Normal file
28
netbox/ipam/migrations/0071_prefix_scope.py
Normal file
@ -0,0 +1,28 @@
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0193_poweroutlet_color'),
|
||||
('ipam', '0070_vlangroup_vlan_id_ranges'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='prefix',
|
||||
name='location',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='prefixes', to='dcim.location'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='prefix',
|
||||
name='region',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='prefixes', to='dcim.region'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='prefix',
|
||||
name='site_group',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='prefixes', to='dcim.sitegroup'),
|
||||
),
|
||||
]
|
@ -1,4 +1,5 @@
|
||||
import netaddr
|
||||
from django.apps import apps
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
@ -207,6 +208,20 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel):
|
||||
verbose_name=_('prefix'),
|
||||
help_text=_('IPv4 or IPv6 network with mask')
|
||||
)
|
||||
region = models.ForeignKey(
|
||||
to='dcim.Region',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='prefixes',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
site_group = models.ForeignKey(
|
||||
to='dcim.SiteGroup',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='prefixes',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
site = models.ForeignKey(
|
||||
to='dcim.Site',
|
||||
on_delete=models.PROTECT,
|
||||
@ -214,6 +229,13 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel):
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
location = models.ForeignKey(
|
||||
to='dcim.Location',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='prefixes',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
vrf = models.ForeignKey(
|
||||
to='ipam.VRF',
|
||||
on_delete=models.PROTECT,
|
||||
@ -275,7 +297,8 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel):
|
||||
objects = PrefixQuerySet.as_manager()
|
||||
|
||||
clone_fields = (
|
||||
'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description',
|
||||
'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description',
|
||||
# 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description', 'scope_type', 'scope',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@ -341,6 +364,27 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, PrimaryModel):
|
||||
def children(self):
|
||||
return self._children
|
||||
|
||||
@property
|
||||
def scope_type(self):
|
||||
if not self.scope:
|
||||
return None
|
||||
return ObjectType.objects.get_for_model(self.scope)
|
||||
|
||||
@property
|
||||
def scope(self):
|
||||
return self.region or self.site_group or self.site or self.location
|
||||
|
||||
@scope.setter
|
||||
def scope(self, value):
|
||||
self.region = self.site_group = self.site = self.location = None
|
||||
if value is not None:
|
||||
if value._meta.model_name == 'sitegroup':
|
||||
# TODO: Fix this hack
|
||||
field_name = 'site_group'
|
||||
else:
|
||||
field_name = value._meta.model_name
|
||||
setattr(self, field_name, value)
|
||||
|
||||
def _set_prefix_length(self, value):
|
||||
"""
|
||||
Expose the IPNetwork object's prefixlen attribute on the parent model so that it can be manipulated directly,
|
||||
|
@ -241,10 +241,30 @@ class PrefixTable(TenancyColumnsMixin, NetBoxTable):
|
||||
template_code=VRF_LINK,
|
||||
verbose_name=_('VRF')
|
||||
)
|
||||
scope_type = columns.ContentTypeColumn(
|
||||
verbose_name=_('Scope Type'),
|
||||
)
|
||||
scope = tables.Column(
|
||||
linkify=True,
|
||||
orderable=False,
|
||||
verbose_name=_('Scope')
|
||||
)
|
||||
region = tables.Column(
|
||||
verbose_name=_('Region'),
|
||||
linkify=True
|
||||
)
|
||||
site_group = tables.Column(
|
||||
verbose_name=_('Site Group'),
|
||||
linkify=True
|
||||
)
|
||||
site = tables.Column(
|
||||
verbose_name=_('Site'),
|
||||
linkify=True
|
||||
)
|
||||
location = tables.Column(
|
||||
verbose_name=_('Location'),
|
||||
linkify=True
|
||||
)
|
||||
vlan_group = tables.Column(
|
||||
accessor='vlan__group',
|
||||
linkify=True,
|
||||
@ -285,11 +305,11 @@ class PrefixTable(TenancyColumnsMixin, NetBoxTable):
|
||||
model = Prefix
|
||||
fields = (
|
||||
'pk', 'id', 'prefix', 'prefix_flat', 'status', 'children', 'vrf', 'utilization', 'tenant', 'tenant_group',
|
||||
'site', 'vlan_group', 'vlan', 'role', 'is_pool', 'mark_utilized', 'description', 'comments', 'tags',
|
||||
'created', 'last_updated',
|
||||
'region', 'site_group', 'site', 'location', 'vlan_group', 'vlan', 'role', 'is_pool', 'mark_utilized',
|
||||
'description', 'comments', 'tags', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'prefix', 'status', 'children', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description',
|
||||
'pk', 'prefix', 'status', 'children', 'vrf', 'utilization', 'tenant', 'vlan', 'role', 'description',
|
||||
)
|
||||
row_attrs = {
|
||||
'class': lambda record: 'success' if not record.pk else '',
|
||||
|
@ -656,14 +656,14 @@ class PrefixTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
Tenant.objects.bulk_create(tenants)
|
||||
|
||||
prefixes = (
|
||||
Prefix(prefix='10.0.0.0/24', tenant=None, site=None, vrf=None, vlan=None, role=None, is_pool=True, mark_utilized=True, description='foobar1'),
|
||||
Prefix(prefix='10.0.1.0/24', tenant=tenants[0], site=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0], description='foobar2'),
|
||||
Prefix(prefix='10.0.2.0/24', tenant=tenants[1], site=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED),
|
||||
Prefix(prefix='10.0.3.0/24', tenant=tenants[2], site=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED),
|
||||
Prefix(prefix='2001:db8::/64', tenant=None, site=None, vrf=None, vlan=None, role=None, is_pool=True, mark_utilized=True),
|
||||
Prefix(prefix='2001:db8:0:1::/64', tenant=tenants[0], site=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0]),
|
||||
Prefix(prefix='2001:db8:0:2::/64', tenant=tenants[1], site=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED),
|
||||
Prefix(prefix='2001:db8:0:3::/64', tenant=tenants[2], site=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED),
|
||||
Prefix(prefix='10.0.0.0/24', tenant=None, scope=None, vrf=None, vlan=None, role=None, is_pool=True, mark_utilized=True, description='foobar1'),
|
||||
Prefix(prefix='10.0.1.0/24', tenant=tenants[0], scope=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0], description='foobar2'),
|
||||
Prefix(prefix='10.0.2.0/24', tenant=tenants[1], scope=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED),
|
||||
Prefix(prefix='10.0.3.0/24', tenant=tenants[2], scope=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED),
|
||||
Prefix(prefix='2001:db8::/64', tenant=None, scope=None, vrf=None, vlan=None, role=None, is_pool=True, mark_utilized=True),
|
||||
Prefix(prefix='2001:db8:0:1::/64', tenant=tenants[0], scope=sites[0], vrf=vrfs[0], vlan=vlans[0], role=roles[0]),
|
||||
Prefix(prefix='2001:db8:0:2::/64', tenant=tenants[1], scope=sites[1], vrf=vrfs[1], vlan=vlans[1], role=roles[1], status=PrefixStatusChoices.STATUS_DEPRECATED),
|
||||
Prefix(prefix='2001:db8:0:3::/64', tenant=tenants[2], scope=sites[2], vrf=vrfs[2], vlan=vlans[2], role=roles[2], status=PrefixStatusChoices.STATUS_RESERVED),
|
||||
Prefix(prefix='10.0.0.0/16'),
|
||||
Prefix(prefix='2001:db8::/32'),
|
||||
)
|
||||
|
@ -409,9 +409,9 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
Role.objects.bulk_create(roles)
|
||||
|
||||
prefixes = (
|
||||
Prefix(prefix=IPNetwork('10.1.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
|
||||
Prefix(prefix=IPNetwork('10.2.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
|
||||
Prefix(prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
|
||||
Prefix(prefix=IPNetwork('10.1.0.0/16'), vrf=vrfs[0], scope=sites[0], role=roles[0]),
|
||||
Prefix(prefix=IPNetwork('10.2.0.0/16'), vrf=vrfs[0], scope=sites[0], role=roles[0]),
|
||||
Prefix(prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], scope=sites[0], role=roles[0]),
|
||||
)
|
||||
Prefix.objects.bulk_create(prefixes)
|
||||
|
||||
|
@ -352,7 +352,7 @@ class AggregatePrefixesView(generic.ObjectChildrenView):
|
||||
def get_children(self, request, parent):
|
||||
return Prefix.objects.restrict(request.user, 'view').filter(
|
||||
prefix__net_contained_or_equal=str(parent.prefix)
|
||||
).prefetch_related('site', 'role', 'tenant', 'tenant__group', 'vlan')
|
||||
).prefetch_related('region', 'site_group', 'site', 'location', 'role', 'tenant', 'tenant__group', 'vlan')
|
||||
|
||||
def prep_table_data(self, request, queryset, parent):
|
||||
# Determine whether to show assigned prefixes, available prefixes, or both
|
||||
@ -467,7 +467,7 @@ class RoleBulkDeleteView(generic.BulkDeleteView):
|
||||
#
|
||||
|
||||
class PrefixListView(generic.ObjectListView):
|
||||
queryset = Prefix.objects.all()
|
||||
queryset = Prefix.objects.prefetch_related('region', 'site_group', 'site', 'location')
|
||||
filterset = filtersets.PrefixFilterSet
|
||||
filterset_form = forms.PrefixFilterForm
|
||||
table = tables.PrefixTable
|
||||
@ -492,7 +492,7 @@ class PrefixView(generic.ObjectView):
|
||||
).filter(
|
||||
prefix__net_contains=str(instance.prefix)
|
||||
).prefetch_related(
|
||||
'site', 'role', 'tenant', 'vlan',
|
||||
'region', 'site_group', 'site', 'location', 'role', 'tenant', 'vlan',
|
||||
)
|
||||
parent_prefix_table = tables.PrefixTable(
|
||||
list(parent_prefixes),
|
||||
@ -506,7 +506,7 @@ class PrefixView(generic.ObjectView):
|
||||
).exclude(
|
||||
pk=instance.pk
|
||||
).prefetch_related(
|
||||
'site', 'role', 'tenant', 'vlan',
|
||||
'region', 'site_group', 'site', 'location', 'role', 'tenant', 'vlan',
|
||||
)
|
||||
duplicate_prefix_table = tables.PrefixTable(
|
||||
list(duplicate_prefixes),
|
||||
@ -538,7 +538,7 @@ class PrefixPrefixesView(generic.ObjectChildrenView):
|
||||
|
||||
def get_children(self, request, parent):
|
||||
return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
|
||||
'site', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group'
|
||||
'region', 'site_group', 'site', 'location', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group'
|
||||
)
|
||||
|
||||
def prep_table_data(self, request, queryset, parent):
|
||||
@ -631,14 +631,14 @@ class PrefixBulkImportView(generic.BulkImportView):
|
||||
|
||||
|
||||
class PrefixBulkEditView(generic.BulkEditView):
|
||||
queryset = Prefix.objects.prefetch_related('vrf__tenant')
|
||||
queryset = Prefix.objects.prefetch_related('region', 'site_group', 'site', 'location')
|
||||
filterset = filtersets.PrefixFilterSet
|
||||
table = tables.PrefixTable
|
||||
form = forms.PrefixBulkEditForm
|
||||
|
||||
|
||||
class PrefixBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = Prefix.objects.prefetch_related('vrf__tenant')
|
||||
queryset = Prefix.objects.prefetch_related('region', 'site_group', 'site', 'location')
|
||||
filterset = filtersets.PrefixFilterSet
|
||||
table = tables.PrefixTable
|
||||
|
||||
|
@ -44,17 +44,13 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if object.site.region %}
|
||||
<tr>
|
||||
<th scope="row">{% trans "Region" %}</th>
|
||||
<td>
|
||||
{% nested_tree object.site.region %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th scope="row">{% trans "Site" %}</th>
|
||||
<td>{{ object.site|linkify|placeholder }}</td>
|
||||
<th scope="row">{% trans "Scope" %}</th>
|
||||
{% if object.scope %}
|
||||
<td>{{ object.scope|linkify }} ({% trans object.scope_type.name %})</td>
|
||||
{% else %}
|
||||
<td>{{ ''|placeholder }}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "VLAN" %}</th>
|
||||
|
Loading…
Reference in New Issue
Block a user