From 45e84befcbe52a6197c385e057d69aab2f4e07ff Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Fri, 21 Apr 2023 09:41:03 -0700 Subject: [PATCH] 12278 add ipaddressfield serializer for OpenAPI spectacular typing (#12285) * #12278 add serializer for ipaddressfield to remove spectacular warnings * #12278 add ipaddressfieldserializer to nested serializers * #12278 fix to_internal_value to_representation in serializer * #12278 to_internal_value is called before validation! need to raise validation error if incorrect format * #12278 to_internal_value needs to return value doh * #12278 move IPAddressField to field_serializers * #12278 remove old import * 12278 remove validator --- netbox/ipam/api/field_serializers.py | 32 +++++++++++++++++++++++++++ netbox/ipam/api/nested_serializers.py | 4 ++++ netbox/ipam/api/serializers.py | 4 ++++ 3 files changed, 40 insertions(+) create mode 100644 netbox/ipam/api/field_serializers.py diff --git a/netbox/ipam/api/field_serializers.py b/netbox/ipam/api/field_serializers.py new file mode 100644 index 000000000..d44d8b7d4 --- /dev/null +++ b/netbox/ipam/api/field_serializers.py @@ -0,0 +1,32 @@ +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from ipam import models +from netaddr import AddrFormatError, IPNetwork + +__all__ = [ + 'IPAddressField', +] + + +# +# IP address field +# + +class IPAddressField(serializers.CharField): + """IPAddressField with mask""" + + default_error_messages = { + 'invalid': _('Enter a valid IPv4 or IPv6 address with optional mask.'), + } + + def to_internal_value(self, data): + try: + return IPNetwork(data) + except AddrFormatError: + raise serializers.ValidationError("Invalid IP address format: {}".format(data)) + except (TypeError, ValueError) as e: + raise serializers.ValidationError(e) + + def to_representation(self, value): + return str(value) diff --git a/netbox/ipam/api/nested_serializers.py b/netbox/ipam/api/nested_serializers.py index 57bb58b5e..9e150e2cb 100644 --- a/netbox/ipam/api/nested_serializers.py +++ b/netbox/ipam/api/nested_serializers.py @@ -4,6 +4,7 @@ from rest_framework import serializers from ipam import models from ipam.models.l2vpn import L2VPNTermination, L2VPN from netbox.api.serializers import WritableNestedSerializer +from .field_serializers import IPAddressField __all__ = [ 'NestedAggregateSerializer', @@ -182,6 +183,8 @@ class NestedPrefixSerializer(WritableNestedSerializer): class NestedIPRangeSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:iprange-detail') family = serializers.IntegerField(read_only=True) + start_address = IPAddressField() + end_address = IPAddressField() class Meta: model = models.IPRange @@ -195,6 +198,7 @@ class NestedIPRangeSerializer(WritableNestedSerializer): class NestedIPAddressSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail') family = serializers.IntegerField(read_only=True) + address = IPAddressField() class Meta: model = models.IPAddress diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index 6339ca60c..064452667 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -13,6 +13,7 @@ from tenancy.api.nested_serializers import NestedTenantSerializer from utilities.api import get_serializer_for_model from virtualization.api.nested_serializers import NestedVirtualMachineSerializer from .nested_serializers import * +from .field_serializers import IPAddressField # @@ -369,6 +370,8 @@ class AvailablePrefixSerializer(serializers.Serializer): class IPRangeSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:iprange-detail') family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True) + start_address = IPAddressField() + end_address = IPAddressField() vrf = NestedVRFSerializer(required=False, allow_null=True) tenant = NestedTenantSerializer(required=False, allow_null=True) status = ChoiceField(choices=IPRangeStatusChoices, required=False) @@ -391,6 +394,7 @@ class IPRangeSerializer(NetBoxModelSerializer): class IPAddressSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail') family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True) + address = IPAddressField() vrf = NestedVRFSerializer(required=False, allow_null=True) tenant = NestedTenantSerializer(required=False, allow_null=True) status = ChoiceField(choices=IPAddressStatusChoices, required=False)