Просмотр исходного кода

Adopt django-taggit-serializer for representation of assigned tags in the API

Jeremy Stretch 7 лет назад
Родитель
Сommit
f1bc88fc0c

+ 1 - 0
base_requirements.txt

@@ -7,6 +7,7 @@ django-filter==1.1.0
 django-mptt
 django-mptt
 django-tables2
 django-tables2
 django-taggit
 django-taggit
+django-taggit-serializer
 django-timezone-field
 django-timezone-field
 # https://github.com/encode/django-rest-framework/issues/6053
 # https://github.com/encode/django-rest-framework/issues/6053
 djangorestframework==3.8.1
 djangorestframework==3.8.1

+ 6 - 6
netbox/circuits/api/serializers.py

@@ -1,22 +1,22 @@
 from __future__ import unicode_literals
 from __future__ import unicode_literals
 
 
 from rest_framework import serializers
 from rest_framework import serializers
-from taggit.models import Tag
+from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
 
 from circuits.constants import CIRCUIT_STATUS_CHOICES
 from circuits.constants import CIRCUIT_STATUS_CHOICES
 from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
 from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
 from dcim.api.serializers import NestedSiteSerializer, InterfaceSerializer
 from dcim.api.serializers import NestedSiteSerializer, InterfaceSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 from tenancy.api.serializers import NestedTenantSerializer
 from tenancy.api.serializers import NestedTenantSerializer
-from utilities.api import ChoiceField, TagField, ValidatedModelSerializer, WritableNestedSerializer
+from utilities.api import ChoiceField, ValidatedModelSerializer, WritableNestedSerializer
 
 
 
 
 #
 #
 # Providers
 # Providers
 #
 #
 
 
-class ProviderSerializer(CustomFieldModelSerializer):
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+class ProviderSerializer(TaggitSerializer, CustomFieldModelSerializer):
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = Provider
         model = Provider
@@ -57,12 +57,12 @@ class NestedCircuitTypeSerializer(WritableNestedSerializer):
 # Circuits
 # Circuits
 #
 #
 
 
-class CircuitSerializer(CustomFieldModelSerializer):
+class CircuitSerializer(TaggitSerializer, CustomFieldModelSerializer):
     provider = NestedProviderSerializer()
     provider = NestedProviderSerializer()
     status = ChoiceField(choices=CIRCUIT_STATUS_CHOICES, required=False)
     status = ChoiceField(choices=CIRCUIT_STATUS_CHOICES, required=False)
     type = NestedCircuitTypeSerializer()
     type = NestedCircuitTypeSerializer()
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = Circuit
         model = Circuit

+ 26 - 26
netbox/dcim/api/serializers.py

@@ -2,7 +2,7 @@ from __future__ import unicode_literals
 
 
 from rest_framework import serializers
 from rest_framework import serializers
 from rest_framework.validators import UniqueTogetherValidator
 from rest_framework.validators import UniqueTogetherValidator
-from taggit.models import Tag
+from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
 
 from circuits.models import Circuit, CircuitTermination
 from circuits.models import Circuit, CircuitTermination
 from dcim.constants import (
 from dcim.constants import (
@@ -20,7 +20,7 @@ from ipam.models import IPAddress, VLAN
 from tenancy.api.serializers import NestedTenantSerializer
 from tenancy.api.serializers import NestedTenantSerializer
 from users.api.serializers import NestedUserSerializer
 from users.api.serializers import NestedUserSerializer
 from utilities.api import (
 from utilities.api import (
-    ChoiceField, SerializedPKRelatedField, TagField, TimeZoneField, ValidatedModelSerializer,
+    ChoiceField, SerializedPKRelatedField, TimeZoneField, ValidatedModelSerializer,
     WritableNestedSerializer,
     WritableNestedSerializer,
 )
 )
 from virtualization.models import Cluster
 from virtualization.models import Cluster
@@ -50,12 +50,12 @@ class RegionSerializer(serializers.ModelSerializer):
 # Sites
 # Sites
 #
 #
 
 
-class SiteSerializer(CustomFieldModelSerializer):
+class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
     status = ChoiceField(choices=SITE_STATUS_CHOICES, required=False)
     status = ChoiceField(choices=SITE_STATUS_CHOICES, required=False)
     region = NestedRegionSerializer(required=False, allow_null=True)
     region = NestedRegionSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     time_zone = TimeZoneField(required=False)
     time_zone = TimeZoneField(required=False)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = Site
         model = Site
@@ -118,14 +118,14 @@ class NestedRackRoleSerializer(WritableNestedSerializer):
 # Racks
 # Racks
 #
 #
 
 
-class RackSerializer(CustomFieldModelSerializer):
+class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
     site = NestedSiteSerializer()
     site = NestedSiteSerializer()
     group = NestedRackGroupSerializer(required=False, allow_null=True)
     group = NestedRackGroupSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     role = NestedRackRoleSerializer(required=False, allow_null=True)
     role = NestedRackRoleSerializer(required=False, allow_null=True)
     type = ChoiceField(choices=RACK_TYPE_CHOICES, required=False)
     type = ChoiceField(choices=RACK_TYPE_CHOICES, required=False)
     width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False)
     width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = Rack
         model = Rack
@@ -220,12 +220,12 @@ class NestedManufacturerSerializer(WritableNestedSerializer):
 # Device types
 # Device types
 #
 #
 
 
-class DeviceTypeSerializer(CustomFieldModelSerializer):
+class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
     manufacturer = NestedManufacturerSerializer()
     manufacturer = NestedManufacturerSerializer()
     interface_ordering = ChoiceField(choices=IFACE_ORDERING_CHOICES, required=False)
     interface_ordering = ChoiceField(choices=IFACE_ORDERING_CHOICES, required=False)
     subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False)
     subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False)
     instance_count = serializers.IntegerField(source='instances.count', read_only=True)
     instance_count = serializers.IntegerField(source='instances.count', read_only=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = DeviceType
         model = DeviceType
@@ -389,7 +389,7 @@ class DeviceVirtualChassisSerializer(serializers.ModelSerializer):
         fields = ['id', 'url', 'master']
         fields = ['id', 'url', 'master']
 
 
 
 
-class DeviceSerializer(CustomFieldModelSerializer):
+class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
     device_type = NestedDeviceTypeSerializer()
     device_type = NestedDeviceTypeSerializer()
     device_role = NestedDeviceRoleSerializer()
     device_role = NestedDeviceRoleSerializer()
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
@@ -404,7 +404,7 @@ class DeviceSerializer(CustomFieldModelSerializer):
     parent_device = serializers.SerializerMethodField()
     parent_device = serializers.SerializerMethodField()
     cluster = NestedClusterSerializer(required=False, allow_null=True)
     cluster = NestedClusterSerializer(required=False, allow_null=True)
     virtual_chassis = DeviceVirtualChassisSerializer(required=False, allow_null=True)
     virtual_chassis = DeviceVirtualChassisSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = Device
         model = Device
@@ -459,9 +459,9 @@ class DeviceWithConfigContextSerializer(DeviceSerializer):
 # Console server ports
 # Console server ports
 #
 #
 
 
-class ConsoleServerPortSerializer(ValidatedModelSerializer):
+class ConsoleServerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = ConsoleServerPort
         model = ConsoleServerPort
@@ -482,10 +482,10 @@ class NestedConsoleServerPortSerializer(WritableNestedSerializer):
 # Console ports
 # Console ports
 #
 #
 
 
-class ConsolePortSerializer(ValidatedModelSerializer):
+class ConsolePortSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     cs_port = NestedConsoleServerPortSerializer(required=False, allow_null=True)
     cs_port = NestedConsoleServerPortSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = ConsolePort
         model = ConsolePort
@@ -496,9 +496,9 @@ class ConsolePortSerializer(ValidatedModelSerializer):
 # Power outlets
 # Power outlets
 #
 #
 
 
-class PowerOutletSerializer(ValidatedModelSerializer):
+class PowerOutletSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = PowerOutlet
         model = PowerOutlet
@@ -519,10 +519,10 @@ class NestedPowerOutletSerializer(WritableNestedSerializer):
 # Power ports
 # Power ports
 #
 #
 
 
-class PowerPortSerializer(ValidatedModelSerializer):
+class PowerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     power_outlet = NestedPowerOutletSerializer(required=False, allow_null=True)
     power_outlet = NestedPowerOutletSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = PowerPort
         model = PowerPort
@@ -569,7 +569,7 @@ class InterfaceVLANSerializer(WritableNestedSerializer):
         fields = ['id', 'url', 'vid', 'name', 'display_name']
         fields = ['id', 'url', 'vid', 'name', 'display_name']
 
 
 
 
-class InterfaceSerializer(ValidatedModelSerializer):
+class InterfaceSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     form_factor = ChoiceField(choices=IFACE_FF_CHOICES, required=False)
     form_factor = ChoiceField(choices=IFACE_FF_CHOICES, required=False)
     lag = NestedInterfaceSerializer(required=False, allow_null=True)
     lag = NestedInterfaceSerializer(required=False, allow_null=True)
@@ -584,7 +584,7 @@ class InterfaceSerializer(ValidatedModelSerializer):
         required=False,
         required=False,
         many=True
         many=True
     )
     )
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = Interface
         model = Interface
@@ -640,10 +640,10 @@ class InterfaceSerializer(ValidatedModelSerializer):
 # Device bays
 # Device bays
 #
 #
 
 
-class DeviceBaySerializer(ValidatedModelSerializer):
+class DeviceBaySerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     installed_device = NestedDeviceSerializer(required=False, allow_null=True)
     installed_device = NestedDeviceSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = DeviceBay
         model = DeviceBay
@@ -662,12 +662,12 @@ class NestedDeviceBaySerializer(WritableNestedSerializer):
 # Inventory items
 # Inventory items
 #
 #
 
 
-class InventoryItemSerializer(ValidatedModelSerializer):
+class InventoryItemSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     # Provide a default value to satisfy UniqueTogetherValidator
     # Provide a default value to satisfy UniqueTogetherValidator
     parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
     parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
     manufacturer = NestedManufacturerSerializer()
     manufacturer = NestedManufacturerSerializer()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = InventoryItem
         model = InventoryItem
@@ -718,9 +718,9 @@ class ContextualInterfaceConnectionSerializer(serializers.ModelSerializer):
 # Virtual chassis
 # Virtual chassis
 #
 #
 
 
-class VirtualChassisSerializer(ValidatedModelSerializer):
+class VirtualChassisSerializer(TaggitSerializer, ValidatedModelSerializer):
     master = NestedDeviceSerializer()
     master = NestedDeviceSerializer()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = VirtualChassis
         model = VirtualChassis

+ 12 - 12
netbox/ipam/api/serializers.py

@@ -5,7 +5,7 @@ from collections import OrderedDict
 from rest_framework import serializers
 from rest_framework import serializers
 from rest_framework.reverse import reverse
 from rest_framework.reverse import reverse
 from rest_framework.validators import UniqueTogetherValidator
 from rest_framework.validators import UniqueTogetherValidator
-from taggit.models import Tag
+from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
 
 from dcim.api.serializers import NestedDeviceSerializer, InterfaceSerializer, NestedSiteSerializer
 from dcim.api.serializers import NestedDeviceSerializer, InterfaceSerializer, NestedSiteSerializer
 from dcim.models import Interface
 from dcim.models import Interface
@@ -16,7 +16,7 @@ from ipam.constants import (
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from tenancy.api.serializers import NestedTenantSerializer
 from tenancy.api.serializers import NestedTenantSerializer
 from utilities.api import (
 from utilities.api import (
-    ChoiceField, SerializedPKRelatedField, TagField, ValidatedModelSerializer, WritableNestedSerializer,
+    ChoiceField, SerializedPKRelatedField, ValidatedModelSerializer, WritableNestedSerializer,
 )
 )
 from virtualization.api.serializers import NestedVirtualMachineSerializer
 from virtualization.api.serializers import NestedVirtualMachineSerializer
 
 
@@ -25,9 +25,9 @@ from virtualization.api.serializers import NestedVirtualMachineSerializer
 # VRFs
 # VRFs
 #
 #
 
 
-class VRFSerializer(CustomFieldModelSerializer):
+class VRFSerializer(TaggitSerializer, CustomFieldModelSerializer):
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = VRF
         model = VRF
@@ -87,9 +87,9 @@ class NestedRIRSerializer(WritableNestedSerializer):
 # Aggregates
 # Aggregates
 #
 #
 
 
-class AggregateSerializer(CustomFieldModelSerializer):
+class AggregateSerializer(TaggitSerializer, CustomFieldModelSerializer):
     rir = NestedRIRSerializer()
     rir = NestedRIRSerializer()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = Aggregate
         model = Aggregate
@@ -147,13 +147,13 @@ class NestedVLANGroupSerializer(WritableNestedSerializer):
 # VLANs
 # VLANs
 #
 #
 
 
-class VLANSerializer(CustomFieldModelSerializer):
+class VLANSerializer(TaggitSerializer, CustomFieldModelSerializer):
     site = NestedSiteSerializer(required=False, allow_null=True)
     site = NestedSiteSerializer(required=False, allow_null=True)
     group = NestedVLANGroupSerializer(required=False, allow_null=True)
     group = NestedVLANGroupSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     status = ChoiceField(choices=VLAN_STATUS_CHOICES, required=False)
     status = ChoiceField(choices=VLAN_STATUS_CHOICES, required=False)
     role = NestedRoleSerializer(required=False, allow_null=True)
     role = NestedRoleSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = VLAN
         model = VLAN
@@ -190,14 +190,14 @@ class NestedVLANSerializer(WritableNestedSerializer):
 # Prefixes
 # Prefixes
 #
 #
 
 
-class PrefixSerializer(CustomFieldModelSerializer):
+class PrefixSerializer(TaggitSerializer, CustomFieldModelSerializer):
     site = NestedSiteSerializer(required=False, allow_null=True)
     site = NestedSiteSerializer(required=False, allow_null=True)
     vrf = NestedVRFSerializer(required=False, allow_null=True)
     vrf = NestedVRFSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     vlan = NestedVLANSerializer(required=False, allow_null=True)
     vlan = NestedVLANSerializer(required=False, allow_null=True)
     status = ChoiceField(choices=PREFIX_STATUS_CHOICES, required=False)
     status = ChoiceField(choices=PREFIX_STATUS_CHOICES, required=False)
     role = NestedRoleSerializer(required=False, allow_null=True)
     role = NestedRoleSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = Prefix
         model = Prefix
@@ -254,13 +254,13 @@ class IPAddressInterfaceSerializer(serializers.ModelSerializer):
         return reverse(url_name, kwargs={'pk': obj.pk}, request=self.context['request'])
         return reverse(url_name, kwargs={'pk': obj.pk}, request=self.context['request'])
 
 
 
 
-class IPAddressSerializer(CustomFieldModelSerializer):
+class IPAddressSerializer(TaggitSerializer, CustomFieldModelSerializer):
     vrf = NestedVRFSerializer(required=False, allow_null=True)
     vrf = NestedVRFSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     status = ChoiceField(choices=IPADDRESS_STATUS_CHOICES, required=False)
     status = ChoiceField(choices=IPADDRESS_STATUS_CHOICES, required=False)
     role = ChoiceField(choices=IPADDRESS_ROLE_CHOICES, required=False)
     role = ChoiceField(choices=IPADDRESS_ROLE_CHOICES, required=False)
     interface = IPAddressInterfaceSerializer(required=False, allow_null=True)
     interface = IPAddressInterfaceSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = IPAddress
         model = IPAddress

+ 1 - 0
netbox/netbox/settings.py

@@ -144,6 +144,7 @@ INSTALLED_APPS = [
     'mptt',
     'mptt',
     'rest_framework',
     'rest_framework',
     'taggit',
     'taggit',
+    'taggit_serializer',
     'timezone_field',
     'timezone_field',
     'circuits',
     'circuits',
     'dcim',
     'dcim',

+ 4 - 4
netbox/secrets/api/serializers.py

@@ -2,12 +2,12 @@ from __future__ import unicode_literals
 
 
 from rest_framework import serializers
 from rest_framework import serializers
 from rest_framework.validators import UniqueTogetherValidator
 from rest_framework.validators import UniqueTogetherValidator
-from taggit.models import Tag
+from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
 
 from dcim.api.serializers import NestedDeviceSerializer
 from dcim.api.serializers import NestedDeviceSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 from secrets.models import Secret, SecretRole
 from secrets.models import Secret, SecretRole
-from utilities.api import TagField, ValidatedModelSerializer, WritableNestedSerializer
+from utilities.api import ValidatedModelSerializer, WritableNestedSerializer
 
 
 
 
 #
 #
@@ -33,11 +33,11 @@ class NestedSecretRoleSerializer(WritableNestedSerializer):
 # Secrets
 # Secrets
 #
 #
 
 
-class SecretSerializer(CustomFieldModelSerializer):
+class SecretSerializer(TaggitSerializer, CustomFieldModelSerializer):
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     role = NestedSecretRoleSerializer()
     role = NestedSecretRoleSerializer()
     plaintext = serializers.CharField()
     plaintext = serializers.CharField()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = Secret
         model = Secret

+ 4 - 4
netbox/tenancy/api/serializers.py

@@ -1,11 +1,11 @@
 from __future__ import unicode_literals
 from __future__ import unicode_literals
 
 
 from rest_framework import serializers
 from rest_framework import serializers
-from taggit.models import Tag
+from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
 
 from extras.api.customfields import CustomFieldModelSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 from tenancy.models import Tenant, TenantGroup
 from tenancy.models import Tenant, TenantGroup
-from utilities.api import TagField, ValidatedModelSerializer, WritableNestedSerializer
+from utilities.api import ValidatedModelSerializer, WritableNestedSerializer
 
 
 
 
 #
 #
@@ -31,9 +31,9 @@ class NestedTenantGroupSerializer(WritableNestedSerializer):
 # Tenants
 # Tenants
 #
 #
 
 
-class TenantSerializer(CustomFieldModelSerializer):
+class TenantSerializer(TaggitSerializer, CustomFieldModelSerializer):
     group = NestedTenantGroupSerializer(required=False)
     group = NestedTenantGroupSerializer(required=False)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = Tenant
         model = Tenant

+ 3 - 17
netbox/utilities/api.py

@@ -2,7 +2,6 @@ from __future__ import unicode_literals
 
 
 from collections import OrderedDict
 from collections import OrderedDict
 import pytz
 import pytz
-from taggit.models import Tag
 
 
 from django.conf import settings
 from django.conf import settings
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
@@ -13,7 +12,7 @@ from rest_framework.exceptions import APIException
 from rest_framework.permissions import BasePermission
 from rest_framework.permissions import BasePermission
 from rest_framework.relations import PrimaryKeyRelatedField
 from rest_framework.relations import PrimaryKeyRelatedField
 from rest_framework.response import Response
 from rest_framework.response import Response
-from rest_framework.serializers import Field, ModelSerializer, RelatedField, ValidationError
+from rest_framework.serializers import Field, ModelSerializer, ValidationError
 from rest_framework.viewsets import ModelViewSet as _ModelViewSet, ViewSet
 from rest_framework.viewsets import ModelViewSet as _ModelViewSet, ViewSet
 
 
 from .utils import dynamic_import
 from .utils import dynamic_import
@@ -56,20 +55,6 @@ class IsAuthenticatedOrLoginNotRequired(BasePermission):
 # Fields
 # Fields
 #
 #
 
 
-class TagField(RelatedField):
-    """
-    Represent a writable list of Tags associated with an object (use with many=True).
-    """
-    def to_internal_value(self, data):
-        obj = self.parent.parent.instance
-        content_type = ContentType.objects.get_for_model(obj)
-        tag, _ = Tag.objects.get_or_create(content_type=content_type, object_id=obj.pk, name=data)
-        return tag
-
-    def to_representation(self, value):
-        return value.name
-
-
 class ChoiceField(Field):
 class ChoiceField(Field):
     """
     """
     Represent a ChoiceField as {'value': <DB value>, 'label': <string>}.
     Represent a ChoiceField as {'value': <DB value>, 'label': <string>}.
@@ -147,9 +132,10 @@ class ValidatedModelSerializer(ModelSerializer):
     """
     """
     def validate(self, data):
     def validate(self, data):
 
 
-        # Remove custom field data (if any) prior to model validation
+        # Remove custom fields data and tags (if any) prior to model validation
         attrs = data.copy()
         attrs = data.copy()
         attrs.pop('custom_fields', None)
         attrs.pop('custom_fields', None)
+        attrs.pop('tags', None)
 
 
         # Run clean() on an instance of the model
         # Run clean() on an instance of the model
         if self.instance is None:
         if self.instance is None:

+ 2 - 2
netbox/utilities/utils.py

@@ -101,8 +101,8 @@ def serialize_object(obj, extra=None):
         }
         }
 
 
     # Include any tags
     # Include any tags
-    if hasattr(obj, 'tags'):
-        data['tags'] = [tag.name for tag in obj.tags.all()]
+    # if hasattr(obj, 'tags'):
+    #     data['tags'] = [tag.name for tag in obj.tags.all()]
 
 
     # Append any extra data
     # Append any extra data
     if extra is not None:
     if extra is not None:

+ 6 - 6
netbox/virtualization/api/serializers.py

@@ -1,7 +1,7 @@
 from __future__ import unicode_literals
 from __future__ import unicode_literals
 
 
 from rest_framework import serializers
 from rest_framework import serializers
-from taggit.models import Tag
+from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
 
 from dcim.api.serializers import NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer
 from dcim.api.serializers import NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer
 from dcim.constants import IFACE_MODE_CHOICES
 from dcim.constants import IFACE_MODE_CHOICES
@@ -9,7 +9,7 @@ from dcim.models import Interface
 from extras.api.customfields import CustomFieldModelSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 from ipam.models import IPAddress, VLAN
 from ipam.models import IPAddress, VLAN
 from tenancy.api.serializers import NestedTenantSerializer
 from tenancy.api.serializers import NestedTenantSerializer
-from utilities.api import ChoiceField, TagField, ValidatedModelSerializer, WritableNestedSerializer
+from utilities.api import ChoiceField, ValidatedModelSerializer, WritableNestedSerializer
 from virtualization.constants import VM_STATUS_CHOICES
 from virtualization.constants import VM_STATUS_CHOICES
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 
 
@@ -56,11 +56,11 @@ class NestedClusterGroupSerializer(WritableNestedSerializer):
 # Clusters
 # Clusters
 #
 #
 
 
-class ClusterSerializer(CustomFieldModelSerializer):
+class ClusterSerializer(TaggitSerializer, CustomFieldModelSerializer):
     type = NestedClusterTypeSerializer()
     type = NestedClusterTypeSerializer()
     group = NestedClusterGroupSerializer(required=False, allow_null=True)
     group = NestedClusterGroupSerializer(required=False, allow_null=True)
     site = NestedSiteSerializer(required=False, allow_null=True)
     site = NestedSiteSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = Cluster
         model = Cluster
@@ -90,7 +90,7 @@ class VirtualMachineIPAddressSerializer(serializers.ModelSerializer):
         fields = ['id', 'url', 'family', 'address']
         fields = ['id', 'url', 'family', 'address']
 
 
 
 
-class VirtualMachineSerializer(CustomFieldModelSerializer):
+class VirtualMachineSerializer(TaggitSerializer, CustomFieldModelSerializer):
     status = ChoiceField(choices=VM_STATUS_CHOICES, required=False)
     status = ChoiceField(choices=VM_STATUS_CHOICES, required=False)
     cluster = NestedClusterSerializer(required=False, allow_null=True)
     cluster = NestedClusterSerializer(required=False, allow_null=True)
     role = NestedDeviceRoleSerializer(required=False, allow_null=True)
     role = NestedDeviceRoleSerializer(required=False, allow_null=True)
@@ -99,7 +99,7 @@ class VirtualMachineSerializer(CustomFieldModelSerializer):
     primary_ip = VirtualMachineIPAddressSerializer(read_only=True)
     primary_ip = VirtualMachineIPAddressSerializer(read_only=True)
     primary_ip4 = VirtualMachineIPAddressSerializer(required=False, allow_null=True)
     primary_ip4 = VirtualMachineIPAddressSerializer(required=False, allow_null=True)
     primary_ip6 = VirtualMachineIPAddressSerializer(required=False, allow_null=True)
     primary_ip6 = VirtualMachineIPAddressSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
 
     class Meta:
     class Meta:
         model = VirtualMachine
         model = VirtualMachine

+ 1 - 0
requirements.txt

@@ -5,6 +5,7 @@ django-filter==1.1.0
 django-mptt==0.9.1
 django-mptt==0.9.1
 django-tables2==1.21.2
 django-tables2==1.21.2
 django-taggit==0.22.2
 django-taggit==0.22.2
+django-taggit-serializer==0.1.7
 django-timezone-field==2.1
 django-timezone-field==2.1
 djangorestframework==3.8.1
 djangorestframework==3.8.1
 drf-yasg[validation]==1.9.1
 drf-yasg[validation]==1.9.1