Sfoglia il codice sorgente

Introduce NonUniqueModelMultipleChoiceFilter to apply distinct-value matching consistently

Move the DISTINCT ON queryset into a ModelMultipleChoiceFilter subclass which derives it from the filter's to_field_name via get_queryset(), and convert the 25 affected filter declarations to use it.
Jeremy Stretch 5 giorni fa
parent
commit
b5003d9697

+ 27 - 37
netbox/dcim/filtersets.py

@@ -33,6 +33,7 @@ from utilities.filters import (
     MultiValueMACAddressFilter,
     MultiValueMACAddressFilter,
     MultiValueTimeZoneFilter,
     MultiValueTimeZoneFilter,
     MultiValueWWNFilter,
     MultiValueWWNFilter,
+    NonUniqueModelMultipleChoiceFilter,
     NumericArrayFilter,
     NumericArrayFilter,
     TreeNodeMultipleChoiceFilter,
     TreeNodeMultipleChoiceFilter,
 )
 )
@@ -286,10 +287,9 @@ class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, NestedGroupMode
         distinct=False,
         distinct=False,
         label=_('Parent location (ID)'),
         label=_('Parent location (ID)'),
     )
     )
-    # Location slugs aren't unique, so match on distinct slugs to avoid redundant predicates
-    parent = django_filters.ModelMultipleChoiceFilter(
+    parent = NonUniqueModelMultipleChoiceFilter(
         field_name='parent__slug',
         field_name='parent__slug',
-        queryset=Location.objects.order_by('slug').distinct('slug'),
+        queryset=Location.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='slug',
         to_field_name='slug',
         label=_('Parent location (slug)'),
         label=_('Parent location (slug)'),
@@ -1847,10 +1847,9 @@ class ModuleFilterSet(PrimaryModelFilterSet):
         distinct=False,
         distinct=False,
         label=_('Location (ID)'),
         label=_('Location (ID)'),
     )
     )
-    # Location slugs aren't unique, so match on distinct slugs to avoid redundant predicates
-    location = django_filters.ModelMultipleChoiceFilter(
+    location = NonUniqueModelMultipleChoiceFilter(
         field_name='device__location__slug',
         field_name='device__location__slug',
-        queryset=Location.objects.order_by('slug').distinct('slug'),
+        queryset=Location.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='slug',
         to_field_name='slug',
         label=_('Location (slug)'),
         label=_('Location (slug)'),
@@ -1861,10 +1860,9 @@ class ModuleFilterSet(PrimaryModelFilterSet):
         distinct=False,
         distinct=False,
         label=_('Rack (ID)'),
         label=_('Rack (ID)'),
     )
     )
-    # Rack names aren't unique, so match on distinct names to avoid redundant predicates
-    rack = django_filters.ModelMultipleChoiceFilter(
+    rack = NonUniqueModelMultipleChoiceFilter(
         field_name='device__rack__name',
         field_name='device__rack__name',
-        queryset=Rack.objects.order_by('name').distinct('name'),
+        queryset=Rack.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='name',
         to_field_name='name',
         label=_('Rack (name)'),
         label=_('Rack (name)'),
@@ -1874,10 +1872,9 @@ class ModuleFilterSet(PrimaryModelFilterSet):
         distinct=False,
         distinct=False,
         label=_('Device (ID)'),
         label=_('Device (ID)'),
     )
     )
-    # Device names aren't unique, so match on distinct names to avoid redundant predicates
-    device = django_filters.ModelMultipleChoiceFilter(
+    device = NonUniqueModelMultipleChoiceFilter(
         field_name='device__name',
         field_name='device__name',
-        queryset=Device.objects.order_by('name').distinct('name'),
+        queryset=Device.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='name',
         to_field_name='name',
         label=_('Device (name)'),
         label=_('Device (name)'),
@@ -1957,10 +1954,9 @@ class DeviceComponentFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
         distinct=False,
         distinct=False,
         label=_('Location (ID)'),
         label=_('Location (ID)'),
     )
     )
-    # Location slugs aren't unique, so match on distinct slugs to avoid redundant predicates
-    location = django_filters.ModelMultipleChoiceFilter(
+    location = NonUniqueModelMultipleChoiceFilter(
         field_name='_location__slug',
         field_name='_location__slug',
-        queryset=Location.objects.order_by('slug').distinct('slug'),
+        queryset=Location.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='slug',
         to_field_name='slug',
         label=_('Location (slug)'),
         label=_('Location (slug)'),
@@ -1971,10 +1967,9 @@ class DeviceComponentFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
         distinct=False,
         distinct=False,
         label=_('Rack (ID)'),
         label=_('Rack (ID)'),
     )
     )
-    # Rack names aren't unique, so match on distinct names to avoid redundant predicates
-    rack = django_filters.ModelMultipleChoiceFilter(
+    rack = NonUniqueModelMultipleChoiceFilter(
         field_name='_rack__name',
         field_name='_rack__name',
-        queryset=Rack.objects.order_by('name').distinct('name'),
+        queryset=Rack.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='name',
         to_field_name='name',
         label=_('Rack (name)'),
         label=_('Rack (name)'),
@@ -1984,10 +1979,9 @@ class DeviceComponentFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
         distinct=False,
         distinct=False,
         label=_('Device (ID)'),
         label=_('Device (ID)'),
     )
     )
-    # Device names aren't unique, so match on distinct names to avoid redundant predicates
-    device = django_filters.ModelMultipleChoiceFilter(
+    device = NonUniqueModelMultipleChoiceFilter(
         field_name='device__name',
         field_name='device__name',
-        queryset=Device.objects.order_by('name').distinct('name'),
+        queryset=Device.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='name',
         to_field_name='name',
         label=_('Device (name)'),
         label=_('Device (name)'),
@@ -2262,10 +2256,9 @@ class MACAddressFilterSet(PrimaryModelFilterSet):
         field_name='pk',
         field_name='pk',
         label=_('Virtual machine (ID)'),
         label=_('Virtual machine (ID)'),
     )
     )
-    # Interface names aren't unique, so match on distinct names to avoid redundant predicates
-    interface = django_filters.ModelMultipleChoiceFilter(
+    interface = NonUniqueModelMultipleChoiceFilter(
         field_name='interface__name',
         field_name='interface__name',
-        queryset=Interface.objects.order_by('name').distinct('name'),
+        queryset=Interface.objects.all(),
         to_field_name='name',
         to_field_name='name',
         label=_('Interface (name)'),
         label=_('Interface (name)'),
     )
     )
@@ -2274,9 +2267,9 @@ class MACAddressFilterSet(PrimaryModelFilterSet):
         queryset=Interface.objects.all(),
         queryset=Interface.objects.all(),
         label=_('Interface (ID)'),
         label=_('Interface (ID)'),
     )
     )
-    vminterface = django_filters.ModelMultipleChoiceFilter(
+    vminterface = NonUniqueModelMultipleChoiceFilter(
         field_name='vminterface__name',
         field_name='vminterface__name',
-        queryset=VMInterface.objects.order_by('name').distinct('name'),
+        queryset=VMInterface.objects.all(),
         to_field_name='name',
         to_field_name='name',
         label=_('VM interface (name)'),
         label=_('VM interface (name)'),
     )
     )
@@ -2524,16 +2517,15 @@ class InterfaceFilterSet(
         queryset=VirtualDeviceContext.objects.all(),
         queryset=VirtualDeviceContext.objects.all(),
         label=_('Virtual Device Context')
         label=_('Virtual Device Context')
     )
     )
-    # VDC names and identifiers aren't unique, so match on distinct values to avoid redundant predicates
-    vdc_identifier = django_filters.ModelMultipleChoiceFilter(
+    vdc_identifier = NonUniqueModelMultipleChoiceFilter(
         field_name='vdcs__identifier',
         field_name='vdcs__identifier',
-        queryset=VirtualDeviceContext.objects.order_by('identifier').distinct('identifier'),
+        queryset=VirtualDeviceContext.objects.all(),
         to_field_name='identifier',
         to_field_name='identifier',
         label=_('Virtual Device Context (Identifier)')
         label=_('Virtual Device Context (Identifier)')
     )
     )
-    vdc = django_filters.ModelMultipleChoiceFilter(
+    vdc = NonUniqueModelMultipleChoiceFilter(
         field_name='vdcs__name',
         field_name='vdcs__name',
-        queryset=VirtualDeviceContext.objects.order_by('name').distinct('name'),
+        queryset=VirtualDeviceContext.objects.all(),
         to_field_name='name',
         to_field_name='name',
         label=_('Virtual Device Context')
         label=_('Virtual Device Context')
     )
     )
@@ -2683,10 +2675,9 @@ class DeviceBayFilterSet(DeviceComponentFilterSet):
         distinct=False,
         distinct=False,
         label=_('Installed device (ID)'),
         label=_('Installed device (ID)'),
     )
     )
-    # Device names aren't unique, so match on distinct names to avoid redundant predicates
-    installed_device = django_filters.ModelMultipleChoiceFilter(
+    installed_device = NonUniqueModelMultipleChoiceFilter(
         field_name='installed_device__name',
         field_name='installed_device__name',
-        queryset=Device.objects.order_by('name').distinct('name'),
+        queryset=Device.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='name',
         to_field_name='name',
         label=_('Installed device (name)'),
         label=_('Installed device (name)'),
@@ -2771,10 +2762,9 @@ class VirtualChassisFilterSet(PrimaryModelFilterSet):
         distinct=False,
         distinct=False,
         label=_('Master (ID)'),
         label=_('Master (ID)'),
     )
     )
-    # Device names aren't unique, so match on distinct names to avoid redundant predicates
-    master = django_filters.ModelMultipleChoiceFilter(
+    master = NonUniqueModelMultipleChoiceFilter(
         field_name='master__name',
         field_name='master__name',
-        queryset=Device.objects.order_by('name').distinct('name'),
+        queryset=Device.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='name',
         to_field_name='name',
         label=_('Master (name)'),
         label=_('Master (name)'),

+ 3 - 3
netbox/extras/filtersets.py

@@ -15,6 +15,7 @@ from utilities.filters import (
     MultiValueCharFilter,
     MultiValueCharFilter,
     MultiValueContentTypeFilter,
     MultiValueContentTypeFilter,
     MultiValueNumberFilter,
     MultiValueNumberFilter,
+    NonUniqueModelMultipleChoiceFilter,
 )
 )
 from utilities.filtersets import register_filterset
 from utilities.filtersets import register_filterset
 from virtualization.models import Cluster, ClusterGroup, ClusterType
 from virtualization.models import Cluster, ClusterGroup, ClusterType
@@ -760,10 +761,9 @@ class ConfigContextFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
         queryset=Location.objects.all(),
         queryset=Location.objects.all(),
         label=_('Location'),
         label=_('Location'),
     )
     )
-    # Location slugs aren't unique, so match on distinct slugs to avoid redundant predicates
-    location = django_filters.ModelMultipleChoiceFilter(
+    location = NonUniqueModelMultipleChoiceFilter(
         field_name='locations__slug',
         field_name='locations__slug',
-        queryset=Location.objects.order_by('slug').distinct('slug'),
+        queryset=Location.objects.all(),
         to_field_name='slug',
         to_field_name='slug',
         label=_('Location (slug)'),
         label=_('Location (slug)'),
     )
     )

+ 9 - 11
netbox/ipam/filtersets.py

@@ -22,6 +22,7 @@ from utilities.filters import (
     MultiValueCharFilter,
     MultiValueCharFilter,
     MultiValueContentTypeFilter,
     MultiValueContentTypeFilter,
     MultiValueNumberFilter,
     MultiValueNumberFilter,
+    NonUniqueModelMultipleChoiceFilter,
     TreeNodeMultipleChoiceFilter,
     TreeNodeMultipleChoiceFilter,
 )
 )
 from utilities.filtersets import register_filterset
 from utilities.filtersets import register_filterset
@@ -397,10 +398,9 @@ class PrefixFilterSet(PrimaryModelFilterSet, ScopedFilterSet, TenancyFilterSet,
         to_field_name='id',
         to_field_name='id',
         label=_('VLAN Group (ID)'),
         label=_('VLAN Group (ID)'),
     )
     )
-    # VLAN group slugs aren't unique, so match on distinct slugs to avoid redundant predicates
-    vlan_group = django_filters.ModelMultipleChoiceFilter(
+    vlan_group = NonUniqueModelMultipleChoiceFilter(
         field_name='vlan__group__slug',
         field_name='vlan__group__slug',
-        queryset=VLANGroup.objects.order_by('slug').distinct('slug'),
+        queryset=VLANGroup.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='slug',
         to_field_name='slug',
         label=_('VLAN Group (slug)'),
         label=_('VLAN Group (slug)'),
@@ -676,10 +676,9 @@ class IPAddressFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFi
         field_name='pk',
         field_name='pk',
         label=_('Virtual machine (ID)'),
         label=_('Virtual machine (ID)'),
     )
     )
-    # Interface names aren't unique, so match on distinct names to avoid redundant predicates
-    interface = django_filters.ModelMultipleChoiceFilter(
+    interface = NonUniqueModelMultipleChoiceFilter(
         field_name='interface__name',
         field_name='interface__name',
-        queryset=Interface.objects.order_by('name').distinct('name'),
+        queryset=Interface.objects.all(),
         to_field_name='name',
         to_field_name='name',
         label=_('Interface (name)'),
         label=_('Interface (name)'),
     )
     )
@@ -688,9 +687,9 @@ class IPAddressFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFi
         queryset=Interface.objects.all(),
         queryset=Interface.objects.all(),
         label=_('Interface (ID)'),
         label=_('Interface (ID)'),
     )
     )
-    vminterface = django_filters.ModelMultipleChoiceFilter(
+    vminterface = NonUniqueModelMultipleChoiceFilter(
         field_name='vminterface__name',
         field_name='vminterface__name',
-        queryset=VMInterface.objects.order_by('name').distinct('name'),
+        queryset=VMInterface.objects.all(),
         to_field_name='name',
         to_field_name='name',
         label=_('VM interface (name)'),
         label=_('VM interface (name)'),
     )
     )
@@ -1048,10 +1047,9 @@ class VLANFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
         distinct=False,
         distinct=False,
         label=_('Group (ID)'),
         label=_('Group (ID)'),
     )
     )
-    # VLAN group slugs aren't unique, so match on distinct slugs to avoid redundant predicates
-    group = django_filters.ModelMultipleChoiceFilter(
+    group = NonUniqueModelMultipleChoiceFilter(
         field_name='group__slug',
         field_name='group__slug',
-        queryset=VLANGroup.objects.order_by('slug').distinct('slug'),
+        queryset=VLANGroup.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='slug',
         to_field_name='slug',
         label=_('Group'),
         label=_('Group'),

+ 10 - 0
netbox/utilities/filters.py

@@ -25,6 +25,7 @@ __all__ = (
     'MultiValueTimeFilter',
     'MultiValueTimeFilter',
     'MultiValueTimeZoneFilter',
     'MultiValueTimeZoneFilter',
     'MultiValueWWNFilter',
     'MultiValueWWNFilter',
+    'NonUniqueModelMultipleChoiceFilter',
     'NullableCharFieldFilter',
     'NullableCharFieldFilter',
     'NumericArrayFilter',
     'NumericArrayFilter',
     'TreeNodeMultipleChoiceFilter',
     'TreeNodeMultipleChoiceFilter',
@@ -177,6 +178,15 @@ class TreeNodeMultipleChoiceFilter(django_filters.ModelMultipleChoiceFilter):
         return super().filter(qs, value)
         return super().filter(qs, value)
 
 
 
 
+class NonUniqueModelMultipleChoiceFilter(django_filters.ModelMultipleChoiceFilter):
+    """
+    A ModelMultipleChoiceFilter for a non-unique to_field_name, which resolves each value to a single object.
+    """
+    def get_queryset(self, request):
+        to_field_name = self.extra['to_field_name']
+        return super().get_queryset(request).order_by(to_field_name).distinct(to_field_name)
+
+
 class NullableCharFieldFilter(django_filters.CharFilter):
 class NullableCharFieldFilter(django_filters.CharFilter):
     """
     """
     Allow matching on null field values by passing a special string used to signify NULL.
     Allow matching on null field values by passing a special string used to signify NULL.

+ 8 - 4
netbox/virtualization/filtersets.py

@@ -13,7 +13,12 @@ from ipam.filtersets import PrimaryIPFilterSet
 from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
 from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
 from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
 from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
 from users.filterset_mixins import OwnerFilterMixin
 from users.filterset_mixins import OwnerFilterMixin
-from utilities.filters import MultiValueCharFilter, MultiValueMACAddressFilter, TreeNodeMultipleChoiceFilter
+from utilities.filters import (
+    MultiValueCharFilter,
+    MultiValueMACAddressFilter,
+    NonUniqueModelMultipleChoiceFilter,
+    TreeNodeMultipleChoiceFilter,
+)
 from utilities.filtersets import register_filterset
 from utilities.filtersets import register_filterset
 
 
 from .choices import *
 from .choices import *
@@ -204,10 +209,9 @@ class VirtualMachineFilterSet(
         distinct=False,
         distinct=False,
         label=_('Device (ID)'),
         label=_('Device (ID)'),
     )
     )
-    # Device names aren't unique, so match on distinct names to avoid redundant predicates
-    device = django_filters.ModelMultipleChoiceFilter(
+    device = NonUniqueModelMultipleChoiceFilter(
         field_name='device__name',
         field_name='device__name',
-        queryset=Device.objects.order_by('name').distinct('name'),
+        queryset=Device.objects.all(),
         distinct=False,
         distinct=False,
         to_field_name='name',
         to_field_name='name',
         label=_('Device'),
         label=_('Device'),

+ 18 - 17
netbox/vpn/filtersets.py

@@ -7,7 +7,12 @@ from dcim.models import Device, Interface
 from ipam.models import VLAN, IPAddress, RouteTarget
 from ipam.models import VLAN, IPAddress, RouteTarget
 from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
 from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
 from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
 from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
-from utilities.filters import MultiValueBigNumberFilter, MultiValueCharFilter, MultiValueContentTypeFilter
+from utilities.filters import (
+    MultiValueBigNumberFilter,
+    MultiValueCharFilter,
+    MultiValueContentTypeFilter,
+    NonUniqueModelMultipleChoiceFilter,
+)
 from utilities.filtersets import register_filterset
 from utilities.filtersets import register_filterset
 from virtualization.models import VirtualMachine, VMInterface
 from virtualization.models import VirtualMachine, VMInterface
 
 
@@ -105,10 +110,9 @@ class TunnelTerminationFilterSet(NetBoxModelFilterSet):
         distinct=False,
         distinct=False,
     )
     )
     termination_type = MultiValueContentTypeFilter()
     termination_type = MultiValueContentTypeFilter()
-    # Interface names aren't unique, so match on distinct names to avoid redundant predicates
-    interface = django_filters.ModelMultipleChoiceFilter(
+    interface = NonUniqueModelMultipleChoiceFilter(
         field_name='interface__name',
         field_name='interface__name',
-        queryset=Interface.objects.order_by('name').distinct('name'),
+        queryset=Interface.objects.all(),
         to_field_name='name',
         to_field_name='name',
         label=_('Interface (name)'),
         label=_('Interface (name)'),
     )
     )
@@ -117,9 +121,9 @@ class TunnelTerminationFilterSet(NetBoxModelFilterSet):
         queryset=Interface.objects.all(),
         queryset=Interface.objects.all(),
         label=_('Interface (ID)'),
         label=_('Interface (ID)'),
     )
     )
-    vminterface = django_filters.ModelMultipleChoiceFilter(
+    vminterface = NonUniqueModelMultipleChoiceFilter(
         field_name='vminterface__name',
         field_name='vminterface__name',
-        queryset=VMInterface.objects.order_by('name').distinct('name'),
+        queryset=VMInterface.objects.all(),
         to_field_name='name',
         to_field_name='name',
         label=_('VM interface (name)'),
         label=_('VM interface (name)'),
     )
     )
@@ -417,10 +421,9 @@ class L2VPNTerminationFilterSet(NetBoxModelFilterSet):
         field_name='pk',
         field_name='pk',
         label=_('Site (ID)'),
         label=_('Site (ID)'),
     )
     )
-    # Device names aren't unique, so match on distinct names to avoid redundant predicates
-    device = django_filters.ModelMultipleChoiceFilter(
+    device = NonUniqueModelMultipleChoiceFilter(
         field_name='interface__device__name',
         field_name='interface__device__name',
-        queryset=Device.objects.order_by('name').distinct('name'),
+        queryset=Device.objects.all(),
         to_field_name='name',
         to_field_name='name',
         label=_('Device (name)'),
         label=_('Device (name)'),
     )
     )
@@ -440,10 +443,9 @@ class L2VPNTerminationFilterSet(NetBoxModelFilterSet):
         queryset=VirtualMachine.objects.all(),
         queryset=VirtualMachine.objects.all(),
         label=_('Virtual machine (ID)'),
         label=_('Virtual machine (ID)'),
     )
     )
-    # Interface names aren't unique, so match on distinct names to avoid redundant predicates
-    interface = django_filters.ModelMultipleChoiceFilter(
+    interface = NonUniqueModelMultipleChoiceFilter(
         field_name='interface__name',
         field_name='interface__name',
-        queryset=Interface.objects.order_by('name').distinct('name'),
+        queryset=Interface.objects.all(),
         to_field_name='name',
         to_field_name='name',
         label=_('Interface (name)'),
         label=_('Interface (name)'),
     )
     )
@@ -452,9 +454,9 @@ class L2VPNTerminationFilterSet(NetBoxModelFilterSet):
         queryset=Interface.objects.all(),
         queryset=Interface.objects.all(),
         label=_('Interface (ID)'),
         label=_('Interface (ID)'),
     )
     )
-    vminterface = django_filters.ModelMultipleChoiceFilter(
+    vminterface = NonUniqueModelMultipleChoiceFilter(
         field_name='vminterface__name',
         field_name='vminterface__name',
-        queryset=VMInterface.objects.order_by('name').distinct('name'),
+        queryset=VMInterface.objects.all(),
         to_field_name='name',
         to_field_name='name',
         label=_('VM interface (name)'),
         label=_('VM interface (name)'),
     )
     )
@@ -463,10 +465,9 @@ class L2VPNTerminationFilterSet(NetBoxModelFilterSet):
         queryset=VMInterface.objects.all(),
         queryset=VMInterface.objects.all(),
         label=_('VM Interface (ID)'),
         label=_('VM Interface (ID)'),
     )
     )
-    # VLAN names aren't unique, so match on distinct names to avoid redundant predicates
-    vlan = django_filters.ModelMultipleChoiceFilter(
+    vlan = NonUniqueModelMultipleChoiceFilter(
         field_name='vlan__name',
         field_name='vlan__name',
-        queryset=VLAN.objects.order_by('name').distinct('name'),
+        queryset=VLAN.objects.all(),
         to_field_name='name',
         to_field_name='name',
         label=_('VLAN (name)'),
         label=_('VLAN (name)'),
     )
     )