Răsfoiți Sursa

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

Jeremy Stretch 7 ani în urmă
părinte
comite
f1bc88fc0c

+ 1 - 0
base_requirements.txt

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

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

@@ -1,22 +1,22 @@
 from __future__ import unicode_literals
 
 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.models import Provider, Circuit, CircuitTermination, CircuitType
 from dcim.api.serializers import NestedSiteSerializer, InterfaceSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 from tenancy.api.serializers import NestedTenantSerializer
-from utilities.api import ChoiceField, TagField, ValidatedModelSerializer, WritableNestedSerializer
+from utilities.api import ChoiceField, ValidatedModelSerializer, WritableNestedSerializer
 
 
 #
 # Providers
 #
 
-class ProviderSerializer(CustomFieldModelSerializer):
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+class ProviderSerializer(TaggitSerializer, CustomFieldModelSerializer):
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = Provider
@@ -57,12 +57,12 @@ class NestedCircuitTypeSerializer(WritableNestedSerializer):
 # Circuits
 #
 
-class CircuitSerializer(CustomFieldModelSerializer):
+class CircuitSerializer(TaggitSerializer, CustomFieldModelSerializer):
     provider = NestedProviderSerializer()
     status = ChoiceField(choices=CIRCUIT_STATUS_CHOICES, required=False)
     type = NestedCircuitTypeSerializer()
     tenant = NestedTenantSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         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.validators import UniqueTogetherValidator
-from taggit.models import Tag
+from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
 from circuits.models import Circuit, CircuitTermination
 from dcim.constants import (
@@ -20,7 +20,7 @@ from ipam.models import IPAddress, VLAN
 from tenancy.api.serializers import NestedTenantSerializer
 from users.api.serializers import NestedUserSerializer
 from utilities.api import (
-    ChoiceField, SerializedPKRelatedField, TagField, TimeZoneField, ValidatedModelSerializer,
+    ChoiceField, SerializedPKRelatedField, TimeZoneField, ValidatedModelSerializer,
     WritableNestedSerializer,
 )
 from virtualization.models import Cluster
@@ -50,12 +50,12 @@ class RegionSerializer(serializers.ModelSerializer):
 # Sites
 #
 
-class SiteSerializer(CustomFieldModelSerializer):
+class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
     status = ChoiceField(choices=SITE_STATUS_CHOICES, required=False)
     region = NestedRegionSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     time_zone = TimeZoneField(required=False)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = Site
@@ -118,14 +118,14 @@ class NestedRackRoleSerializer(WritableNestedSerializer):
 # Racks
 #
 
-class RackSerializer(CustomFieldModelSerializer):
+class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
     site = NestedSiteSerializer()
     group = NestedRackGroupSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     role = NestedRackRoleSerializer(required=False, allow_null=True)
     type = ChoiceField(choices=RACK_TYPE_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:
         model = Rack
@@ -220,12 +220,12 @@ class NestedManufacturerSerializer(WritableNestedSerializer):
 # Device types
 #
 
-class DeviceTypeSerializer(CustomFieldModelSerializer):
+class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
     manufacturer = NestedManufacturerSerializer()
     interface_ordering = ChoiceField(choices=IFACE_ORDERING_CHOICES, required=False)
     subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False)
     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:
         model = DeviceType
@@ -389,7 +389,7 @@ class DeviceVirtualChassisSerializer(serializers.ModelSerializer):
         fields = ['id', 'url', 'master']
 
 
-class DeviceSerializer(CustomFieldModelSerializer):
+class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
     device_type = NestedDeviceTypeSerializer()
     device_role = NestedDeviceRoleSerializer()
     tenant = NestedTenantSerializer(required=False, allow_null=True)
@@ -404,7 +404,7 @@ class DeviceSerializer(CustomFieldModelSerializer):
     parent_device = serializers.SerializerMethodField()
     cluster = NestedClusterSerializer(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:
         model = Device
@@ -459,9 +459,9 @@ class DeviceWithConfigContextSerializer(DeviceSerializer):
 # Console server ports
 #
 
-class ConsoleServerPortSerializer(ValidatedModelSerializer):
+class ConsoleServerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = ConsoleServerPort
@@ -482,10 +482,10 @@ class NestedConsoleServerPortSerializer(WritableNestedSerializer):
 # Console ports
 #
 
-class ConsolePortSerializer(ValidatedModelSerializer):
+class ConsolePortSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     cs_port = NestedConsoleServerPortSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = ConsolePort
@@ -496,9 +496,9 @@ class ConsolePortSerializer(ValidatedModelSerializer):
 # Power outlets
 #
 
-class PowerOutletSerializer(ValidatedModelSerializer):
+class PowerOutletSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = PowerOutlet
@@ -519,10 +519,10 @@ class NestedPowerOutletSerializer(WritableNestedSerializer):
 # Power ports
 #
 
-class PowerPortSerializer(ValidatedModelSerializer):
+class PowerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     power_outlet = NestedPowerOutletSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = PowerPort
@@ -569,7 +569,7 @@ class InterfaceVLANSerializer(WritableNestedSerializer):
         fields = ['id', 'url', 'vid', 'name', 'display_name']
 
 
-class InterfaceSerializer(ValidatedModelSerializer):
+class InterfaceSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     form_factor = ChoiceField(choices=IFACE_FF_CHOICES, required=False)
     lag = NestedInterfaceSerializer(required=False, allow_null=True)
@@ -584,7 +584,7 @@ class InterfaceSerializer(ValidatedModelSerializer):
         required=False,
         many=True
     )
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = Interface
@@ -640,10 +640,10 @@ class InterfaceSerializer(ValidatedModelSerializer):
 # Device bays
 #
 
-class DeviceBaySerializer(ValidatedModelSerializer):
+class DeviceBaySerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     installed_device = NestedDeviceSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = DeviceBay
@@ -662,12 +662,12 @@ class NestedDeviceBaySerializer(WritableNestedSerializer):
 # Inventory items
 #
 
-class InventoryItemSerializer(ValidatedModelSerializer):
+class InventoryItemSerializer(TaggitSerializer, ValidatedModelSerializer):
     device = NestedDeviceSerializer()
     # Provide a default value to satisfy UniqueTogetherValidator
     parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
     manufacturer = NestedManufacturerSerializer()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = InventoryItem
@@ -718,9 +718,9 @@ class ContextualInterfaceConnectionSerializer(serializers.ModelSerializer):
 # Virtual chassis
 #
 
-class VirtualChassisSerializer(ValidatedModelSerializer):
+class VirtualChassisSerializer(TaggitSerializer, ValidatedModelSerializer):
     master = NestedDeviceSerializer()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         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.reverse import reverse
 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.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 tenancy.api.serializers import NestedTenantSerializer
 from utilities.api import (
-    ChoiceField, SerializedPKRelatedField, TagField, ValidatedModelSerializer, WritableNestedSerializer,
+    ChoiceField, SerializedPKRelatedField, ValidatedModelSerializer, WritableNestedSerializer,
 )
 from virtualization.api.serializers import NestedVirtualMachineSerializer
 
@@ -25,9 +25,9 @@ from virtualization.api.serializers import NestedVirtualMachineSerializer
 # VRFs
 #
 
-class VRFSerializer(CustomFieldModelSerializer):
+class VRFSerializer(TaggitSerializer, CustomFieldModelSerializer):
     tenant = NestedTenantSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = VRF
@@ -87,9 +87,9 @@ class NestedRIRSerializer(WritableNestedSerializer):
 # Aggregates
 #
 
-class AggregateSerializer(CustomFieldModelSerializer):
+class AggregateSerializer(TaggitSerializer, CustomFieldModelSerializer):
     rir = NestedRIRSerializer()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = Aggregate
@@ -147,13 +147,13 @@ class NestedVLANGroupSerializer(WritableNestedSerializer):
 # VLANs
 #
 
-class VLANSerializer(CustomFieldModelSerializer):
+class VLANSerializer(TaggitSerializer, CustomFieldModelSerializer):
     site = NestedSiteSerializer(required=False, allow_null=True)
     group = NestedVLANGroupSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     status = ChoiceField(choices=VLAN_STATUS_CHOICES, required=False)
     role = NestedRoleSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = VLAN
@@ -190,14 +190,14 @@ class NestedVLANSerializer(WritableNestedSerializer):
 # Prefixes
 #
 
-class PrefixSerializer(CustomFieldModelSerializer):
+class PrefixSerializer(TaggitSerializer, CustomFieldModelSerializer):
     site = NestedSiteSerializer(required=False, allow_null=True)
     vrf = NestedVRFSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     vlan = NestedVLANSerializer(required=False, allow_null=True)
     status = ChoiceField(choices=PREFIX_STATUS_CHOICES, required=False)
     role = NestedRoleSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = Prefix
@@ -254,13 +254,13 @@ class IPAddressInterfaceSerializer(serializers.ModelSerializer):
         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)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     status = ChoiceField(choices=IPADDRESS_STATUS_CHOICES, required=False)
     role = ChoiceField(choices=IPADDRESS_ROLE_CHOICES, required=False)
     interface = IPAddressInterfaceSerializer(required=False, allow_null=True)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = IPAddress

+ 1 - 0
netbox/netbox/settings.py

@@ -144,6 +144,7 @@ INSTALLED_APPS = [
     'mptt',
     'rest_framework',
     'taggit',
+    'taggit_serializer',
     'timezone_field',
     'circuits',
     '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.validators import UniqueTogetherValidator
-from taggit.models import Tag
+from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
 from dcim.api.serializers import NestedDeviceSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 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
 #
 
-class SecretSerializer(CustomFieldModelSerializer):
+class SecretSerializer(TaggitSerializer, CustomFieldModelSerializer):
     device = NestedDeviceSerializer()
     role = NestedSecretRoleSerializer()
     plaintext = serializers.CharField()
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = Secret

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

@@ -1,11 +1,11 @@
 from __future__ import unicode_literals
 
 from rest_framework import serializers
-from taggit.models import Tag
+from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
 from extras.api.customfields import CustomFieldModelSerializer
 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
 #
 
-class TenantSerializer(CustomFieldModelSerializer):
+class TenantSerializer(TaggitSerializer, CustomFieldModelSerializer):
     group = NestedTenantGroupSerializer(required=False)
-    tags = TagField(queryset=Tag.objects.all(), required=False, many=True)
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = Tenant

+ 3 - 17
netbox/utilities/api.py

@@ -2,7 +2,6 @@ from __future__ import unicode_literals
 
 from collections import OrderedDict
 import pytz
-from taggit.models import Tag
 
 from django.conf import settings
 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.relations import PrimaryKeyRelatedField
 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 .utils import dynamic_import
@@ -56,20 +55,6 @@ class IsAuthenticatedOrLoginNotRequired(BasePermission):
 # 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):
     """
     Represent a ChoiceField as {'value': <DB value>, 'label': <string>}.
@@ -147,9 +132,10 @@ class ValidatedModelSerializer(ModelSerializer):
     """
     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.pop('custom_fields', None)
+        attrs.pop('tags', None)
 
         # Run clean() on an instance of the model
         if self.instance is None:

+ 2 - 2
netbox/utilities/utils.py

@@ -101,8 +101,8 @@ def serialize_object(obj, extra=None):
         }
 
     # 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
     if extra is not None:

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

@@ -1,7 +1,7 @@
 from __future__ import unicode_literals
 
 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.constants import IFACE_MODE_CHOICES
@@ -9,7 +9,7 @@ from dcim.models import Interface
 from extras.api.customfields import CustomFieldModelSerializer
 from ipam.models import IPAddress, VLAN
 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.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 
@@ -56,11 +56,11 @@ class NestedClusterGroupSerializer(WritableNestedSerializer):
 # Clusters
 #
 
-class ClusterSerializer(CustomFieldModelSerializer):
+class ClusterSerializer(TaggitSerializer, CustomFieldModelSerializer):
     type = NestedClusterTypeSerializer()
     group = NestedClusterGroupSerializer(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:
         model = Cluster
@@ -90,7 +90,7 @@ class VirtualMachineIPAddressSerializer(serializers.ModelSerializer):
         fields = ['id', 'url', 'family', 'address']
 
 
-class VirtualMachineSerializer(CustomFieldModelSerializer):
+class VirtualMachineSerializer(TaggitSerializer, CustomFieldModelSerializer):
     status = ChoiceField(choices=VM_STATUS_CHOICES, required=False)
     cluster = NestedClusterSerializer(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_ip4 = 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:
         model = VirtualMachine

+ 1 - 0
requirements.txt

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