Closes #20304: Object owners (#20634)

This commit is contained in:
Jeremy Stretch
2025-10-24 16:08:01 -04:00
committed by GitHub
parent 52d4498caf
commit be74436884
196 changed files with 15831 additions and 2715 deletions

View File

@@ -1,11 +1,11 @@
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.constants import LOCATION_SCOPE_TYPES
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from ipam.api.serializers_.vlans import VLANSerializer
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
from netbox.api.serializers import NestedGroupModelSerializer, PrimaryModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from wireless.choices import *
@@ -26,12 +26,12 @@ class WirelessLANGroupSerializer(NestedGroupModelSerializer):
model = WirelessLANGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
'created', 'last_updated', 'wirelesslan_count', 'comments', '_depth',
'created', 'last_updated', 'wirelesslan_count', 'owner', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'wirelesslan_count', '_depth')
class WirelessLANSerializer(NetBoxModelSerializer):
class WirelessLANSerializer(PrimaryModelSerializer):
group = WirelessLANGroupSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=WirelessLANStatusChoices, required=False, allow_blank=True)
vlan = VLANSerializer(nested=True, required=False, allow_null=True)
@@ -53,8 +53,8 @@ class WirelessLANSerializer(NetBoxModelSerializer):
model = WirelessLAN
fields = [
'id', 'url', 'display_url', 'display', 'ssid', 'description', 'group', 'status', 'vlan', 'scope_type',
'scope_id', 'scope', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
'scope_id', 'scope', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'description', 'owner', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'ssid', 'description')

View File

@@ -1,7 +1,7 @@
from dcim.api.serializers_.device_components import InterfaceSerializer
from dcim.choices import LinkStatusChoices
from netbox.api.fields import ChoiceField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.api.serializers import PrimaryModelSerializer
from netbox.choices import *
from tenancy.api.serializers_.tenants import TenantSerializer
from wireless.choices import *
@@ -12,7 +12,7 @@ __all__ = (
)
class WirelessLinkSerializer(NetBoxModelSerializer):
class WirelessLinkSerializer(PrimaryModelSerializer):
status = ChoiceField(choices=LinkStatusChoices, required=False)
interface_a = InterfaceSerializer(nested=True)
interface_b = InterfaceSerializer(nested=True)
@@ -25,7 +25,7 @@ class WirelessLinkSerializer(NetBoxModelSerializer):
model = WirelessLink
fields = [
'id', 'url', 'display_url', 'display', 'interface_a', 'interface_b', 'ssid', 'status', 'tenant',
'auth_type', 'auth_cipher', 'auth_psk', 'distance', 'distance_unit', 'description',
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'auth_type', 'auth_cipher', 'auth_psk', 'distance', 'distance_unit', 'description', 'owner', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'ssid', 'description')

View File

@@ -5,7 +5,7 @@ from dcim.choices import LinkStatusChoices
from dcim.base_filtersets import ScopedFilterSet
from dcim.models import Interface
from ipam.models import VLAN
from netbox.filtersets import NestedGroupModelFilterSet, NetBoxModelFilterSet
from netbox.filtersets import NestedGroupModelFilterSet, PrimaryModelFilterSet
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import TreeNodeMultipleChoiceFilter
from .choices import *
@@ -44,7 +44,7 @@ class WirelessLANGroupFilterSet(NestedGroupModelFilterSet):
fields = ('id', 'name', 'slug', 'description')
class WirelessLANFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet):
class WirelessLANFilterSet(PrimaryModelFilterSet, ScopedFilterSet, TenancyFilterSet):
group_id = TreeNodeMultipleChoiceFilter(
queryset=WirelessLANGroup.objects.all(),
field_name='group',
@@ -87,7 +87,7 @@ class WirelessLANFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterS
return queryset.filter(qs_filter)
class WirelessLinkFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class WirelessLinkFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
interface_a_id = django_filters.ModelMultipleChoiceFilter(
queryset=Interface.objects.all()
)

View File

@@ -5,10 +5,10 @@ from dcim.choices import LinkStatusChoices
from dcim.forms.mixins import ScopedBulkEditForm
from ipam.models import VLAN
from netbox.choices import *
from netbox.forms import NetBoxModelBulkEditForm
from netbox.forms import NestedGroupModelBulkEditForm, PrimaryModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import add_blank_choice
from utilities.forms.fields import CommentField, DynamicModelChoiceField
from utilities.forms.fields import DynamicModelChoiceField
from utilities.forms.rendering import FieldSet
from wireless.choices import *
from wireless.constants import SSID_MAX_LENGTH
@@ -21,18 +21,12 @@ __all__ = (
)
class WirelessLANGroupBulkEditForm(NetBoxModelBulkEditForm):
class WirelessLANGroupBulkEditForm(NestedGroupModelBulkEditForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=WirelessLANGroup.objects.all(),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = WirelessLANGroup
fieldsets = (
@@ -41,7 +35,7 @@ class WirelessLANGroupBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('parent', 'description', 'comments')
class WirelessLANBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
class WirelessLANBulkEditForm(ScopedBulkEditForm, PrimaryModelBulkEditForm):
status = forms.ChoiceField(
label=_('Status'),
choices=add_blank_choice(WirelessLANStatusChoices),
@@ -81,12 +75,6 @@ class WirelessLANBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
required=False,
label=_('Pre-shared key')
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = WirelessLAN
fieldsets = (
@@ -99,7 +87,7 @@ class WirelessLANBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
)
class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
class WirelessLinkBulkEditForm(PrimaryModelBulkEditForm):
ssid = forms.CharField(
max_length=SSID_MAX_LENGTH,
required=False,
@@ -140,12 +128,6 @@ class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
required=False,
initial=''
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
comments = CommentField()
model = WirelessLink
fieldsets = (

View File

@@ -5,9 +5,9 @@ from dcim.forms.mixins import ScopedImportForm
from dcim.models import Device, Interface, Site
from ipam.models import VLAN
from netbox.choices import *
from netbox.forms import NetBoxModelImportForm
from netbox.forms import NestedGroupModelImportForm, PrimaryModelImportForm
from tenancy.models import Tenant
from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField
from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField
from wireless.choices import *
from wireless.models import *
@@ -18,7 +18,7 @@ __all__ = (
)
class WirelessLANGroupImportForm(NetBoxModelImportForm):
class WirelessLANGroupImportForm(NestedGroupModelImportForm):
parent = CSVModelChoiceField(
label=_('Parent'),
queryset=WirelessLANGroup.objects.all(),
@@ -26,14 +26,13 @@ class WirelessLANGroupImportForm(NetBoxModelImportForm):
to_field_name='name',
help_text=_('Parent group')
)
slug = SlugField()
class Meta:
model = WirelessLANGroup
fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments')
fields = ('name', 'slug', 'parent', 'description', 'owner', 'comments', 'tags')
class WirelessLANImportForm(ScopedImportForm, NetBoxModelImportForm):
class WirelessLANImportForm(ScopedImportForm, PrimaryModelImportForm):
group = CSVModelChoiceField(
label=_('Group'),
queryset=WirelessLANGroup.objects.all(),
@@ -77,14 +76,14 @@ class WirelessLANImportForm(ScopedImportForm, NetBoxModelImportForm):
model = WirelessLAN
fields = (
'ssid', 'group', 'status', 'vlan', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'scope_type',
'scope_id', 'description', 'comments', 'tags',
'scope_id', 'description', 'owner', 'comments', 'tags',
)
labels = {
'scope_id': _('Scope ID'),
}
class WirelessLinkImportForm(NetBoxModelImportForm):
class WirelessLinkImportForm(PrimaryModelImportForm):
# Termination A
site_a = CSVModelChoiceField(
label=_('Site A'),
@@ -163,7 +162,8 @@ class WirelessLinkImportForm(NetBoxModelImportForm):
model = WirelessLink
fields = (
'site_a', 'device_a', 'interface_a', 'site_b', 'device_b', 'interface_b', 'status', 'ssid', 'tenant',
'auth_type', 'auth_cipher', 'auth_psk', 'distance', 'distance_unit', 'description', 'comments', 'tags',
'auth_type', 'auth_cipher', 'auth_psk', 'distance', 'distance_unit', 'description', 'owner', 'comments',
'tags',
)
def __init__(self, data=None, *args, **kwargs):

View File

@@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _
from dcim.choices import LinkStatusChoices
from dcim.models import Location, Region, Site, SiteGroup
from netbox.choices import *
from netbox.forms import NetBoxModelFilterSetForm
from netbox.forms import NestedGroupModelFilterSetForm, PrimaryModelFilterSetForm
from tenancy.forms import TenancyFilterForm
from utilities.forms import add_blank_choice
from utilities.forms.fields import DynamicModelMultipleChoiceField, TagFilterField
@@ -19,8 +19,12 @@ __all__ = (
)
class WirelessLANGroupFilterForm(NetBoxModelFilterSetForm):
class WirelessLANGroupFilterForm(NestedGroupModelFilterSetForm):
model = WirelessLANGroup
fieldsets = (
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('parent_id', name=_('Wireless LAN group')),
)
parent_id = DynamicModelMultipleChoiceField(
queryset=WirelessLANGroup.objects.all(),
required=False,
@@ -29,10 +33,10 @@ class WirelessLANGroupFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)
class WirelessLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
class WirelessLANFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = WirelessLAN
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('ssid', 'group_id', 'status', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Scope')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
@@ -95,10 +99,10 @@ class WirelessLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
tag = TagFilterField(model)
class WirelessLinkFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
class WirelessLinkFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = WirelessLink
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
FieldSet('q', 'filter_id', 'tag', 'owner_id'),
FieldSet('ssid', 'status', 'distance', 'distance_unit', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('auth_type', 'auth_cipher', 'auth_psk', name=_('Authentication')),

View File

@@ -1,12 +1,12 @@
from django.forms import PasswordInput
from django.utils.translation import gettext_lazy as _
from dcim.models import Device, Interface, Location, Site
from dcim.forms.mixins import ScopedForm
from dcim.models import Device, Interface, Location, Site
from ipam.models import VLAN
from netbox.forms import NetBoxModelForm
from netbox.forms import NestedGroupModelForm, PrimaryModelForm
from tenancy.forms import TenancyForm
from utilities.forms.fields import CommentField, DynamicModelChoiceField, SlugField
from utilities.forms.fields import DynamicModelChoiceField
from utilities.forms.mixins import DistanceValidationMixin
from utilities.forms.rendering import FieldSet, InlineFields
from wireless.models import *
@@ -18,14 +18,12 @@ __all__ = (
)
class WirelessLANGroupForm(NetBoxModelForm):
class WirelessLANGroupForm(NestedGroupModelForm):
parent = DynamicModelChoiceField(
label=_('Parent'),
queryset=WirelessLANGroup.objects.all(),
required=False
)
slug = SlugField()
comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags', name=_('Wireless LAN Group')),
@@ -34,11 +32,11 @@ class WirelessLANGroupForm(NetBoxModelForm):
class Meta:
model = WirelessLANGroup
fields = [
'parent', 'name', 'slug', 'description', 'tags', 'comments',
'parent', 'name', 'slug', 'description', 'owner', 'comments', 'tags',
]
class WirelessLANForm(ScopedForm, TenancyForm, NetBoxModelForm):
class WirelessLANForm(ScopedForm, TenancyForm, PrimaryModelForm):
group = DynamicModelChoiceField(
label=_('Group'),
queryset=WirelessLANGroup.objects.all(),
@@ -51,7 +49,6 @@ class WirelessLANForm(ScopedForm, TenancyForm, NetBoxModelForm):
selector=True,
label=_('VLAN')
)
comments = CommentField()
fieldsets = (
FieldSet('ssid', 'group', 'vlan', 'status', 'description', 'tags', name=_('Wireless LAN')),
@@ -64,7 +61,7 @@ class WirelessLANForm(ScopedForm, TenancyForm, NetBoxModelForm):
model = WirelessLAN
fields = [
'ssid', 'group', 'status', 'vlan', 'tenant_group', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk',
'scope_type', 'description', 'comments', 'tags',
'scope_type', 'description', 'owner', 'comments', 'tags',
]
widgets = {
'auth_psk': PasswordInput(
@@ -74,7 +71,7 @@ class WirelessLANForm(ScopedForm, TenancyForm, NetBoxModelForm):
}
class WirelessLinkForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
class WirelessLinkForm(DistanceValidationMixin, TenancyForm, PrimaryModelForm):
site_a = DynamicModelChoiceField(
queryset=Site.objects.all(),
required=False,
@@ -159,7 +156,6 @@ class WirelessLinkForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
},
label=_('Interface')
)
comments = CommentField()
fieldsets = (
FieldSet('site_a', 'location_a', 'device_a', 'interface_a', name=_('Side A')),
@@ -181,7 +177,7 @@ class WirelessLinkForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
fields = [
'site_a', 'location_a', 'device_a', 'interface_a', 'site_b', 'location_b', 'device_b', 'interface_b',
'status', 'ssid', 'tenant_group', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk',
'distance', 'distance_unit', 'description', 'comments', 'tags',
'distance', 'distance_unit', 'description', 'owner', 'comments', 'tags',
]
widgets = {
'auth_psk': PasswordInput(

View File

@@ -3,7 +3,7 @@ from typing import Annotated, List, TYPE_CHECKING, Union
import strawberry
import strawberry_django
from netbox.graphql.types import OrganizationalObjectType, NetBoxObjectType
from netbox.graphql.types import NestedGroupObjectType, PrimaryObjectType
from wireless import models
from .filters import *
@@ -25,7 +25,7 @@ __all__ = (
filters=WirelessLANGroupFilter,
pagination=True
)
class WirelessLANGroupType(OrganizationalObjectType):
class WirelessLANGroupType(NestedGroupObjectType):
parent: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None
wireless_lans: List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]]
@@ -38,7 +38,7 @@ class WirelessLANGroupType(OrganizationalObjectType):
filters=WirelessLANFilter,
pagination=True
)
class WirelessLANType(NetBoxObjectType):
class WirelessLANType(PrimaryObjectType):
group: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None
vlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
@@ -61,7 +61,7 @@ class WirelessLANType(NetBoxObjectType):
filters=WirelessLinkFilter,
pagination=True
)
class WirelessLinkType(NetBoxObjectType):
class WirelessLinkType(PrimaryObjectType):
interface_a: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]
interface_b: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None

View File

@@ -0,0 +1,33 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0015_owner'),
('wireless', '0015_extend_wireless_link_abs_distance_upper_limit'),
]
operations = [
migrations.AddField(
model_name='wirelesslan',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='wirelesslangroup',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
migrations.AddField(
model_name='wirelesslink',
name='owner',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.owner'
),
),
]

View File

@@ -1,8 +1,8 @@
from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from dcim.models import Interface
from netbox.tables import NetBoxTable, columns
from netbox.tables import NestedGroupModelTable, NetBoxTable, PrimaryModelTable, columns
from tenancy.tables import TenancyColumnsMixin
from wireless.models import *
@@ -13,28 +13,17 @@ __all__ = (
)
class WirelessLANGroupTable(NetBoxTable):
name = columns.MPTTColumn(
verbose_name=_('Name'),
linkify=True
)
parent = tables.Column(
verbose_name=_('Parent'),
linkify=True,
)
class WirelessLANGroupTable(NestedGroupModelTable):
wirelesslan_count = columns.LinkedCountColumn(
viewname='wireless:wirelesslan_list',
url_params={'group_id': 'pk'},
verbose_name=_('Wireless LANs')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='wireless:wirelesslangroup_list'
)
class Meta(NetBoxTable.Meta):
class Meta(NestedGroupModelTable.Meta):
model = WirelessLANGroup
fields = (
'pk', 'name', 'parent', 'slug', 'description', 'comments', 'tags', 'wirelesslan_count', 'created',
@@ -43,7 +32,7 @@ class WirelessLANGroupTable(NetBoxTable):
default_columns = ('pk', 'name', 'wirelesslan_count', 'description')
class WirelessLANTable(TenancyColumnsMixin, NetBoxTable):
class WirelessLANTable(TenancyColumnsMixin, PrimaryModelTable):
ssid = tables.Column(
verbose_name=_('SSID'),
linkify=True
@@ -66,14 +55,11 @@ class WirelessLANTable(TenancyColumnsMixin, NetBoxTable):
interface_count = tables.Column(
verbose_name=_('Interfaces')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
)
tags = columns.TagColumn(
url_name='wireless:wirelesslan_list'
)
class Meta(NetBoxTable.Meta):
class Meta(PrimaryModelTable.Meta):
model = WirelessLAN
fields = (
'pk', 'ssid', 'group', 'status', 'tenant', 'tenant_group', 'vlan', 'interface_count', 'auth_type',

View File

@@ -1,7 +1,7 @@
from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from netbox.tables import NetBoxTable, columns
from netbox.tables import PrimaryModelTable, columns
from tenancy.tables import TenancyColumnsMixin
from wireless.models import *
@@ -10,7 +10,7 @@ __all__ = (
)
class WirelessLinkTable(TenancyColumnsMixin, NetBoxTable):
class WirelessLinkTable(TenancyColumnsMixin, PrimaryModelTable):
id = tables.Column(
linkify=True,
verbose_name=_('ID')
@@ -41,7 +41,7 @@ class WirelessLinkTable(TenancyColumnsMixin, NetBoxTable):
url_name='wireless:wirelesslink_list'
)
class Meta(NetBoxTable.Meta):
class Meta(PrimaryModelTable.Meta):
model = WirelessLink
fields = (
'pk', 'id', 'status', 'device_a', 'interface_a', 'device_b', 'interface_b', 'ssid', 'tenant',