diff --git a/netbox/templates/tenancy/contactassignment_edit.html b/netbox/templates/tenancy/contactassignment_edit.html
index 4d1747e72..d904deead 100644
--- a/netbox/templates/tenancy/contactassignment_edit.html
+++ b/netbox/templates/tenancy/contactassignment_edit.html
@@ -3,6 +3,9 @@
{% load form_helpers %}
{% block form %}
+ {% for field in form.hidden_fields %}
+ {{ field }}
+ {% endfor %}
Contact Assignment
diff --git a/netbox/tenancy/forms/models.py b/netbox/tenancy/forms/models.py
index 021e36a5b..eabcb1d0f 100644
--- a/netbox/tenancy/forms/models.py
+++ b/netbox/tenancy/forms/models.py
@@ -119,8 +119,10 @@ class ContactAssignmentForm(BootstrapMixin, forms.ModelForm):
class Meta:
model = ContactAssignment
fields = (
- 'group', 'contact', 'role', 'priority',
+ 'content_type', 'object_id', 'group', 'contact', 'role', 'priority',
)
widgets = {
+ 'content_type': forms.HiddenInput(),
+ 'object_id': forms.HiddenInput(),
'priority': StaticSelect(),
}
diff --git a/netbox/tenancy/migrations/0001_squashed_0012.py b/netbox/tenancy/migrations/0001_squashed_0012.py
index 77297b982..e8a028a92 100644
--- a/netbox/tenancy/migrations/0001_squashed_0012.py
+++ b/netbox/tenancy/migrations/0001_squashed_0012.py
@@ -1,4 +1,4 @@
-import django.core.serializers.json
+from utilities.json import CustomFieldJSONEncoder
from django.db import migrations, models
import django.db.models.deletion
import mptt.fields
@@ -34,7 +34,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.SlugField(max_length=100, unique=True)),
@@ -54,7 +54,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.SlugField(max_length=100, unique=True)),
diff --git a/netbox/tenancy/migrations/0003_contacts.py b/netbox/tenancy/migrations/0003_contacts.py
index 35e568ab1..ba9bef50f 100644
--- a/netbox/tenancy/migrations/0003_contacts.py
+++ b/netbox/tenancy/migrations/0003_contacts.py
@@ -1,4 +1,4 @@
-import django.core.serializers.json
+from utilities.json import CustomFieldJSONEncoder
from django.db import migrations, models
import django.db.models.deletion
import mptt.fields
@@ -19,7 +19,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.SlugField(max_length=100, unique=True)),
@@ -34,7 +34,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100)),
('slug', models.SlugField(max_length=100)),
@@ -55,7 +55,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100)),
('title', models.CharField(blank=True, max_length=100)),
diff --git a/netbox/utilities/filters.py b/netbox/utilities/filters.py
index 543449b73..d41eff498 100644
--- a/netbox/utilities/filters.py
+++ b/netbox/utilities/filters.py
@@ -3,8 +3,6 @@ from django import forms
from django.conf import settings
from django_filters.constants import EMPTY_VALUES
-from utilities.forms import MACAddressField
-
def multivalue_field_factory(field_class):
"""
@@ -23,7 +21,15 @@ def multivalue_field_factory(field_class):
field.to_python(v) for v in value if v
]
- return type('MultiValue{}'.format(field_class.__name__), (NewField,), dict())
+ def run_validators(self, value):
+ for v in value:
+ super().run_validators(v)
+
+ def validate(self, value):
+ for v in value:
+ super().validate(v)
+
+ return type(f'MultiValue{field_class.__name__}', (NewField,), dict())
#
@@ -46,6 +52,10 @@ class MultiValueNumberFilter(django_filters.MultipleChoiceFilter):
field_class = multivalue_field_factory(forms.IntegerField)
+class MultiValueDecimalFilter(django_filters.MultipleChoiceFilter):
+ field_class = multivalue_field_factory(forms.DecimalField)
+
+
class MultiValueTimeFilter(django_filters.MultipleChoiceFilter):
field_class = multivalue_field_factory(forms.TimeField)
diff --git a/netbox/utilities/json.py b/netbox/utilities/json.py
new file mode 100644
index 000000000..5574ff36f
--- /dev/null
+++ b/netbox/utilities/json.py
@@ -0,0 +1,17 @@
+import decimal
+
+from django.core.serializers.json import DjangoJSONEncoder
+
+__all__ = (
+ 'CustomFieldJSONEncoder',
+)
+
+
+class CustomFieldJSONEncoder(DjangoJSONEncoder):
+ """
+ Override Django's built-in JSON encoder to save decimal values as JSON numbers.
+ """
+ def default(self, o):
+ if isinstance(o, decimal.Decimal):
+ return float(o)
+ return super().default(o)
diff --git a/netbox/utilities/templatetags/helpers.py b/netbox/utilities/templatetags/helpers.py
index 67ed553b2..462b37feb 100644
--- a/netbox/utilities/templatetags/helpers.py
+++ b/netbox/utilities/templatetags/helpers.py
@@ -73,9 +73,9 @@ def humanize_megabytes(mb):
"""
if not mb:
return ''
- if mb >= 1048576:
+ if not mb % 1048576: # 1024^2
return f'{int(mb / 1048576)} TB'
- if mb >= 1024:
+ if not mb % 1024:
return f'{int(mb / 1024)} GB'
return f'{mb} MB'
diff --git a/netbox/utilities/templatetags/navigation.py b/netbox/utilities/templatetags/navigation.py
index ef0657446..a34ef9816 100644
--- a/netbox/utilities/templatetags/navigation.py
+++ b/netbox/utilities/templatetags/navigation.py
@@ -2,7 +2,7 @@ from typing import Dict
from django import template
from django.template import Context
-from netbox.navigation_menu import MENUS
+from netbox.navigation.menu import MENUS
register = template.Library()
diff --git a/netbox/utilities/tests/test_filters.py b/netbox/utilities/tests/test_filters.py
index 5182722d1..334f270dc 100644
--- a/netbox/utilities/tests/test_filters.py
+++ b/netbox/utilities/tests/test_filters.py
@@ -5,8 +5,6 @@ from django.test import TestCase
from mptt.fields import TreeForeignKey
from taggit.managers import TaggableManager
-from circuits.filtersets import CircuitFilterSet, ProviderFilterSet
-from circuits.models import Circuit, Provider
from dcim.choices import *
from dcim.fields import MACAddressField
from dcim.filtersets import DeviceFilterSet, SiteFilterSet
@@ -15,6 +13,7 @@ from dcim.models import (
)
from extras.filters import TagFilter
from extras.models import TaggedItem
+from ipam.filtersets import ASNFilterSet
from ipam.models import RIR, ASN
from netbox.filtersets import BaseFilterSet
from utilities.filters import (
@@ -338,13 +337,14 @@ class DynamicFilterLookupExpressionTest(TestCase):
"""
@classmethod
def setUpTestData(cls):
+ rir = RIR.objects.create(name='RIR 1', slug='rir-1')
- providers = (
- Provider(name='Provider 1', slug='provider-1', asn=65001),
- Provider(name='Provider 2', slug='provider-2', asn=65101),
- Provider(name='Provider 3', slug='provider-3', asn=65201),
+ asns = (
+ ASN(asn=65001, rir=rir),
+ ASN(asn=65101, rir=rir),
+ ASN(asn=65201, rir=rir),
)
- Provider.objects.bulk_create(providers)
+ ASN.objects.bulk_create(asns)
manufacturers = (
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
@@ -389,15 +389,6 @@ class DynamicFilterLookupExpressionTest(TestCase):
)
Site.objects.bulk_create(sites)
- rir = RIR.objects.create(name='RFC 6996', is_private=True)
-
- asns = [
- ASN(asn=65001, rir=rir),
- ASN(asn=65101, rir=rir),
- ASN(asn=65201, rir=rir)
- ]
- ASN.objects.bulk_create(asns)
-
asns[0].sites.add(sites[0])
asns[1].sites.add(sites[1])
asns[2].sites.add(sites[2])
@@ -456,19 +447,19 @@ class DynamicFilterLookupExpressionTest(TestCase):
def test_provider_asn_lt(self):
params = {'asn__lt': [65101]}
- self.assertEqual(ProviderFilterSet(params, Provider.objects.all()).qs.count(), 1)
+ self.assertEqual(ASNFilterSet(params, ASN.objects.all()).qs.count(), 1)
def test_provider_asn_lte(self):
params = {'asn__lte': [65101]}
- self.assertEqual(ProviderFilterSet(params, Provider.objects.all()).qs.count(), 2)
+ self.assertEqual(ASNFilterSet(params, ASN.objects.all()).qs.count(), 2)
def test_provider_asn_gt(self):
params = {'asn__lt': [65101]}
- self.assertEqual(ProviderFilterSet(params, Provider.objects.all()).qs.count(), 1)
+ self.assertEqual(ASNFilterSet(params, ASN.objects.all()).qs.count(), 1)
def test_provider_asn_gte(self):
params = {'asn__gte': [65101]}
- self.assertEqual(ProviderFilterSet(params, Provider.objects.all()).qs.count(), 2)
+ self.assertEqual(ASNFilterSet(params, ASN.objects.all()).qs.count(), 2)
def test_site_region_negation(self):
params = {'region__n': ['region-1']}
diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py
index 69ab615fc..9f587e88d 100644
--- a/netbox/utilities/utils.py
+++ b/netbox/utilities/utils.py
@@ -12,7 +12,7 @@ from django.http import QueryDict
from jinja2.sandbox import SandboxedEnvironment
from mptt.models import MPTTModel
-from dcim.choices import CableLengthUnitChoices
+from dcim.choices import CableLengthUnitChoices, WeightUnitChoices
from extras.plugins import PluginConfig
from extras.utils import is_taggable
from netbox.config import get_config
@@ -270,6 +270,31 @@ def to_meters(length, unit):
raise ValueError(f"Unknown unit {unit}. Must be 'km', 'm', 'cm', 'mi', 'ft', or 'in'.")
+def to_grams(weight, unit):
+ """
+ Convert the given weight to kilograms.
+ """
+ try:
+ if weight < 0:
+ raise ValueError("Weight must be a positive number")
+ except TypeError:
+ raise TypeError(f"Invalid value '{weight}' for weight (must be a number)")
+
+ valid_units = WeightUnitChoices.values()
+ if unit not in valid_units:
+ raise ValueError(f"Unknown unit {unit}. Must be one of the following: {', '.join(valid_units)}")
+
+ if unit == WeightUnitChoices.UNIT_KILOGRAM:
+ return weight * 1000
+ if unit == WeightUnitChoices.UNIT_GRAM:
+ return weight
+ if unit == WeightUnitChoices.UNIT_POUND:
+ return weight * Decimal(453.592)
+ if unit == WeightUnitChoices.UNIT_OUNCE:
+ return weight * Decimal(28.3495)
+ raise ValueError(f"Unknown unit {unit}. Must be 'kg', 'g', 'lb', 'oz'.")
+
+
def render_jinja2(template_code, context):
"""
Render a Jinja2 template with the provided context. Return the rendered content.
diff --git a/netbox/virtualization/filtersets.py b/netbox/virtualization/filtersets.py
index 00d3e2313..1b9c5bc78 100644
--- a/netbox/virtualization/filtersets.py
+++ b/netbox/virtualization/filtersets.py
@@ -6,7 +6,7 @@ from extras.filtersets import LocalConfigContextFilterSet
from ipam.models import VRF
from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet
from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
-from utilities.filters import MultiValueMACAddressFilter, TreeNodeMultipleChoiceFilter
+from utilities.filters import MultiValueCharFilter, MultiValueMACAddressFilter, TreeNodeMultipleChoiceFilter
from .choices import *
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
@@ -196,6 +196,9 @@ class VirtualMachineFilterSet(
to_field_name='slug',
label='Site (slug)',
)
+ name = MultiValueCharFilter(
+ lookup_expr='iexact'
+ )
role_id = django_filters.ModelMultipleChoiceFilter(
queryset=DeviceRole.objects.all(),
label='Role (ID)',
@@ -227,7 +230,7 @@ class VirtualMachineFilterSet(
class Meta:
model = VirtualMachine
- fields = ['id', 'name', 'cluster', 'vcpus', 'memory', 'disk']
+ fields = ['id', 'cluster', 'vcpus', 'memory', 'disk']
def search(self, queryset, name, value):
if not value.strip():
diff --git a/netbox/virtualization/migrations/0001_squashed_0022.py b/netbox/virtualization/migrations/0001_squashed_0022.py
index d00bae2e2..29eda8a50 100644
--- a/netbox/virtualization/migrations/0001_squashed_0022.py
+++ b/netbox/virtualization/migrations/0001_squashed_0022.py
@@ -1,5 +1,5 @@
import dcim.fields
-import django.core.serializers.json
+from utilities.json import CustomFieldJSONEncoder
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
@@ -51,7 +51,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100, unique=True)),
('comments', models.TextField(blank=True)),
@@ -65,7 +65,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.SlugField(max_length=100, unique=True)),
@@ -80,7 +80,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.SlugField(max_length=100, unique=True)),
@@ -95,7 +95,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('local_context_data', models.JSONField(blank=True, null=True)),
('name', models.CharField(max_length=64)),
@@ -147,7 +147,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('enabled', models.BooleanField(default=True)),
('mac_address', dcim.fields.MACAddressField(blank=True, null=True)),
diff --git a/netbox/virtualization/migrations/0033_unique_constraints.py b/netbox/virtualization/migrations/0033_unique_constraints.py
index 4667dcbd3..0624d3607 100644
--- a/netbox/virtualization/migrations/0033_unique_constraints.py
+++ b/netbox/virtualization/migrations/0033_unique_constraints.py
@@ -1,4 +1,5 @@
from django.db import migrations, models
+import django.db.models.functions.text
class Migration(migrations.Migration):
@@ -30,11 +31,11 @@ class Migration(migrations.Migration):
),
migrations.AddConstraint(
model_name='virtualmachine',
- constraint=models.UniqueConstraint(fields=('name', 'cluster', 'tenant'), name='virtualization_virtualmachine_unique_name_cluster_tenant'),
+ constraint=models.UniqueConstraint(django.db.models.functions.text.Lower('name'), models.F('cluster'), models.F('tenant'), name='virtualization_virtualmachine_unique_name_cluster_tenant'),
),
migrations.AddConstraint(
model_name='virtualmachine',
- constraint=models.UniqueConstraint(condition=models.Q(('tenant__isnull', True)), fields=('name', 'cluster'), name='virtualization_virtualmachine_unique_name_cluster', violation_error_message='Virtual machine name must be unique per site.'),
+ constraint=models.UniqueConstraint(django.db.models.functions.text.Lower('name'), models.F('cluster'), condition=models.Q(('tenant__isnull', True)), name='virtualization_virtualmachine_unique_name_cluster', violation_error_message='Virtual machine name must be unique per cluster.'),
),
migrations.AddConstraint(
model_name='vminterface',
diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py
index 5a1c361c2..37fcd68ae 100644
--- a/netbox/virtualization/models.py
+++ b/netbox/virtualization/models.py
@@ -3,6 +3,7 @@ from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.db import models
from django.db.models import Q
+from django.db.models.functions import Lower
from django.urls import reverse
from dcim.models import BaseInterface, Device
@@ -318,14 +319,14 @@ class VirtualMachine(NetBoxModel, ConfigContextModel):
ordering = ('_name', 'pk') # Name may be non-unique
constraints = (
models.UniqueConstraint(
- fields=('name', 'cluster', 'tenant'),
+ Lower('name'), 'cluster', 'tenant',
name='%(app_label)s_%(class)s_unique_name_cluster_tenant'
),
models.UniqueConstraint(
- fields=('name', 'cluster'),
+ Lower('name'), 'cluster',
name='%(app_label)s_%(class)s_unique_name_cluster',
condition=Q(tenant__isnull=True),
- violation_error_message="Virtual machine name must be unique per site."
+ violation_error_message="Virtual machine name must be unique per cluster."
),
)
diff --git a/netbox/virtualization/tests/test_filtersets.py b/netbox/virtualization/tests/test_filtersets.py
index d3ff12887..d474af21a 100644
--- a/netbox/virtualization/tests/test_filtersets.py
+++ b/netbox/virtualization/tests/test_filtersets.py
@@ -299,6 +299,9 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests):
def test_name(self):
params = {'name': ['Virtual Machine 1', 'Virtual Machine 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+ # Test case insensitivity
+ params = {'name': ['VIRTUAL MACHINE 1', 'VIRTUAL MACHINE 2']}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_vcpus(self):
params = {'vcpus': [1, 2]}
diff --git a/netbox/virtualization/tests/test_models.py b/netbox/virtualization/tests/test_models.py
index df5816efa..bf0571d3d 100644
--- a/netbox/virtualization/tests/test_models.py
+++ b/netbox/virtualization/tests/test_models.py
@@ -8,12 +8,14 @@ from tenancy.models import Tenant
class VirtualMachineTestCase(TestCase):
- def test_vm_duplicate_name_per_cluster(self):
+ @classmethod
+ def setUpTestData(cls):
cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
- cluster = Cluster.objects.create(name='Cluster 1', type=cluster_type)
+ Cluster.objects.create(name='Cluster 1', type=cluster_type)
+ def test_vm_duplicate_name_per_cluster(self):
vm1 = VirtualMachine(
- cluster=cluster,
+ cluster=Cluster.objects.first(),
name='Test VM 1'
)
vm1.save()
@@ -43,7 +45,7 @@ class VirtualMachineTestCase(TestCase):
vm2.save()
def test_vm_mismatched_site_cluster(self):
- cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
+ cluster_type = ClusterType.objects.first()
sites = (
Site(name='Site 1', slug='site-1'),
@@ -71,3 +73,19 @@ class VirtualMachineTestCase(TestCase):
# VM with cluster site but no direct site should fail
with self.assertRaises(ValidationError):
VirtualMachine(name='vm1', site=None, cluster=clusters[0]).full_clean()
+
+ def test_vm_name_case_sensitivity(self):
+ vm1 = VirtualMachine(
+ cluster=Cluster.objects.first(),
+ name='virtual machine 1'
+ )
+ vm1.save()
+
+ vm2 = VirtualMachine(
+ cluster=vm1.cluster,
+ name='VIRTUAL MACHINE 1'
+ )
+
+ # Uniqueness validation for name should ignore case
+ with self.assertRaises(ValidationError):
+ vm2.full_clean()
diff --git a/netbox/wireless/migrations/0001_wireless.py b/netbox/wireless/migrations/0001_wireless.py
index 10b6e585b..9369df8a5 100644
--- a/netbox/wireless/migrations/0001_wireless.py
+++ b/netbox/wireless/migrations/0001_wireless.py
@@ -1,4 +1,4 @@
-import django.core.serializers.json
+from utilities.json import CustomFieldJSONEncoder
from django.db import migrations, models
import django.db.models.deletion
import mptt.fields
@@ -21,7 +21,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.SlugField(max_length=100, unique=True)),
@@ -44,7 +44,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('ssid', models.CharField(max_length=32)),
('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wireless_lans', to='wireless.wirelesslangroup')),
@@ -65,7 +65,7 @@ class Migration(migrations.Migration):
fields=[
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
- ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('ssid', models.CharField(blank=True, max_length=32)),
('status', models.CharField(default='connected', max_length=50)),