Extended API to include custom fields

This commit is contained in:
Jeremy Stretch 2016-08-22 13:20:30 -04:00
parent b14afaa687
commit 76f0463290
14 changed files with 139 additions and 74 deletions

View File

@ -2,6 +2,7 @@ from rest_framework import serializers
from circuits.models import Provider, CircuitType, Circuit
from dcim.api.serializers import SiteNestedSerializer, InterfaceNestedSerializer
from extras.api.serializers import CustomFieldsSerializer
from tenancy.api.serializers import TenantNestedSerializer
@ -9,11 +10,12 @@ from tenancy.api.serializers import TenantNestedSerializer
# Providers
#
class ProviderSerializer(serializers.ModelSerializer):
class ProviderSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
class Meta:
model = Provider
fields = ['id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
fields = ['id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
'custom_fields']
class ProviderNestedSerializer(ProviderSerializer):
@ -43,7 +45,7 @@ class CircuitTypeNestedSerializer(CircuitTypeSerializer):
# Circuits
#
class CircuitSerializer(serializers.ModelSerializer):
class CircuitSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
provider = ProviderNestedSerializer()
type = CircuitTypeNestedSerializer()
tenant = TenantNestedSerializer()
@ -53,7 +55,7 @@ class CircuitSerializer(serializers.ModelSerializer):
class Meta:
model = Circuit
fields = ['id', 'cid', 'provider', 'type', 'tenant', 'site', 'interface', 'install_date', 'port_speed',
'upstream_speed', 'commit_rate', 'xconnect_id', 'comments']
'upstream_speed', 'commit_rate', 'xconnect_id', 'comments', 'custom_fields']
class CircuitNestedSerializer(CircuitSerializer):

View File

@ -3,22 +3,23 @@ from rest_framework import generics
from circuits.models import Provider, CircuitType, Circuit
from circuits.filters import CircuitFilter
from extras.api.views import CustomFieldModelAPIView
from . import serializers
class ProviderListView(generics.ListAPIView):
class ProviderListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List all providers
"""
queryset = Provider.objects.all()
queryset = Provider.objects.prefetch_related('custom_field_values')
serializer_class = serializers.ProviderSerializer
class ProviderDetailView(generics.RetrieveAPIView):
class ProviderDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single provider
"""
queryset = Provider.objects.all()
queryset = Provider.objects.prefetch_related('custom_field_values')
serializer_class = serializers.ProviderSerializer
@ -38,18 +39,20 @@ class CircuitTypeDetailView(generics.RetrieveAPIView):
serializer_class = serializers.CircuitTypeSerializer
class CircuitListView(generics.ListAPIView):
class CircuitListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List circuits (filterable)
"""
queryset = Circuit.objects.select_related('type', 'tenant', 'provider', 'site', 'interface__device')
queryset = Circuit.objects.select_related('type', 'tenant', 'provider', 'site', 'interface__device')\
.prefetch_related('custom_field_values')
serializer_class = serializers.CircuitSerializer
filter_class = CircuitFilter
class CircuitDetailView(generics.RetrieveAPIView):
class CircuitDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single circuit
"""
queryset = Circuit.objects.select_related('type', 'tenant', 'provider', 'site', 'interface__device')
queryset = Circuit.objects.select_related('type', 'tenant', 'provider', 'site', 'interface__device')\
.prefetch_related('custom_field_values')
serializer_class = serializers.CircuitSerializer

View File

@ -1,9 +1,10 @@
from django.contrib.contenttypes.fields import GenericRelation
from django.core.urlresolvers import reverse
from django.db import models
from dcim.fields import ASNField
from dcim.models import Site, Interface
from extras.models import CustomFieldModel
from extras.models import CustomFieldModel, CustomFieldValue
from tenancy.models import Tenant
from utilities.models import CreatedUpdatedModel
@ -21,6 +22,7 @@ class Provider(CreatedUpdatedModel, CustomFieldModel):
noc_contact = models.TextField(blank=True, verbose_name='NOC contact')
admin_contact = models.TextField(blank=True, verbose_name='Admin contact')
comments = models.TextField(blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
class Meta:
ordering = ['name']
@ -79,6 +81,7 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
xconnect_id = models.CharField(max_length=50, blank=True, verbose_name='Cross-connect ID')
pp_info = models.CharField(max_length=100, blank=True, verbose_name='Patch panel/port(s)')
comments = models.TextField(blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
class Meta:
ordering = ['provider', 'cid']

View File

@ -6,6 +6,7 @@ from dcim.models import (
DeviceRole, Interface, InterfaceConnection, InterfaceTemplate, Manufacturer, Module, Platform, PowerOutlet,
PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackRole, RACK_FACE_FRONT, RACK_FACE_REAR, Site,
)
from extras.api.serializers import CustomFieldsSerializer
from tenancy.api.serializers import TenantNestedSerializer
@ -13,13 +14,13 @@ from tenancy.api.serializers import TenantNestedSerializer
# Sites
#
class SiteSerializer(serializers.ModelSerializer):
class SiteSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
tenant = TenantNestedSerializer()
class Meta:
model = Site
fields = ['id', 'name', 'slug', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address', 'comments',
'count_prefixes', 'count_vlans', 'count_racks', 'count_devices', 'count_circuits']
'custom_fields', 'count_prefixes', 'count_vlans', 'count_racks', 'count_devices', 'count_circuits']
class SiteNestedSerializer(SiteSerializer):
@ -68,7 +69,7 @@ class RackRoleNestedSerializer(RackRoleSerializer):
#
class RackSerializer(serializers.ModelSerializer):
class RackSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
site = SiteNestedSerializer()
group = RackGroupNestedSerializer()
tenant = TenantNestedSerializer()
@ -77,7 +78,7 @@ class RackSerializer(serializers.ModelSerializer):
class Meta:
model = Rack
fields = ['id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'type', 'width',
'u_height', 'comments']
'u_height', 'comments', 'custom_fields']
class RackNestedSerializer(RackSerializer):
@ -237,7 +238,7 @@ class DeviceIPAddressNestedSerializer(serializers.ModelSerializer):
fields = ['id', 'family', 'address']
class DeviceSerializer(serializers.ModelSerializer):
class DeviceSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
device_type = DeviceTypeNestedSerializer()
device_role = DeviceRoleNestedSerializer()
tenant = TenantNestedSerializer()
@ -252,7 +253,7 @@ class DeviceSerializer(serializers.ModelSerializer):
model = Device
fields = ['id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial',
'asset_tag', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4',
'primary_ip6', 'comments']
'primary_ip6', 'comments', 'custom_fields']
def get_parent_device(self, obj):
try:

View File

@ -13,29 +13,30 @@ from dcim.models import (
InterfaceConnection, Manufacturer, Module, Platform, PowerOutlet, PowerPort, Rack, RackGroup, RackRole, Site,
)
from dcim import filters
from .exceptions import MissingFilterException
from . import serializers
from extras.api.views import CustomFieldModelAPIView
from extras.api.renderers import BINDZoneRenderer, FlatJSONRenderer
from utilities.api import ServiceUnavailable
from .exceptions import MissingFilterException
from . import serializers
#
# Sites
#
class SiteListView(generics.ListAPIView):
class SiteListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List all sites
"""
queryset = Site.objects.select_related('tenant')
queryset = Site.objects.select_related('tenant').prefetch_related('custom_field_values')
serializer_class = serializers.SiteSerializer
class SiteDetailView(generics.RetrieveAPIView):
class SiteDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single site
"""
queryset = Site.objects.select_related('tenant')
queryset = Site.objects.select_related('tenant').prefetch_related('custom_field_values')
serializer_class = serializers.SiteSerializer
@ -84,20 +85,20 @@ class RackRoleDetailView(generics.RetrieveAPIView):
# Racks
#
class RackListView(generics.ListAPIView):
class RackListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List racks (filterable)
"""
queryset = Rack.objects.select_related('site', 'group', 'tenant')
queryset = Rack.objects.select_related('site', 'group__site', 'tenant').prefetch_related('custom_field_values')
serializer_class = serializers.RackSerializer
filter_class = filters.RackFilter
class RackDetailView(generics.RetrieveAPIView):
class RackDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single rack
"""
queryset = Rack.objects.select_related('site', 'group', 'tenant')
queryset = Rack.objects.select_related('site', 'group__site', 'tenant').prefetch_related('custom_field_values')
serializer_class = serializers.RackDetailSerializer
@ -209,24 +210,25 @@ class PlatformDetailView(generics.RetrieveAPIView):
# Devices
#
class DeviceListView(generics.ListAPIView):
class DeviceListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List devices (filterable)
"""
queryset = Device.objects.select_related('device_type__manufacturer', 'device_role', 'tenant', 'platform',
'rack__site', 'parent_bay').prefetch_related('primary_ip4__nat_outside',
'primary_ip6__nat_outside')
'primary_ip6__nat_outside',
'custom_field_values')
serializer_class = serializers.DeviceSerializer
filter_class = filters.DeviceFilter
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + [BINDZoneRenderer, FlatJSONRenderer]
class DeviceDetailView(generics.RetrieveAPIView):
class DeviceDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single device
"""
queryset = Device.objects.select_related('device_type__manufacturer', 'device_role', 'tenant', 'platform',
'rack__site', 'parent_bay')
'rack__site', 'parent_bay').prefetch_related('custom_field_values')
serializer_class = serializers.DeviceSerializer

View File

@ -2,6 +2,7 @@ from collections import OrderedDict
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericRelation
from django.core.exceptions import MultipleObjectsReturned, ValidationError
from django.core.urlresolvers import reverse
from django.core.validators import MaxValueValidator, MinValueValidator
@ -228,6 +229,7 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
physical_address = models.CharField(max_length=200, blank=True)
shipping_address = models.CharField(max_length=200, blank=True)
comments = models.TextField(blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
objects = SiteManager()
@ -339,6 +341,7 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
u_height = models.PositiveSmallIntegerField(default=42, verbose_name='Height (U)',
validators=[MinValueValidator(1), MaxValueValidator(100)])
comments = models.TextField(blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
objects = RackManager()
@ -752,6 +755,7 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
primary_ip6 = models.OneToOneField('ipam.IPAddress', related_name='primary_ip6_for', on_delete=models.SET_NULL,
blank=True, null=True, verbose_name='Primary IPv6')
comments = models.TextField(blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
objects = DeviceManager()

View File

@ -1,6 +1,29 @@
from rest_framework import serializers
from extras.models import Graph
from extras.models import CF_TYPE_SELECT, CustomFieldChoice, Graph
class CustomFieldsSerializer(serializers.Serializer):
"""
Extends a ModelSerializer to render any CustomFields and their values associated with an object.
"""
custom_fields = serializers.SerializerMethodField()
def get_custom_fields(self, obj):
fields = {cf.name: None for cf in self.context['view'].custom_fields}
for cfv in obj.custom_field_values.all():
if cfv.field.type == CF_TYPE_SELECT:
fields[cfv.field.name] = CustomFieldChoiceSerializer(instance=cfv.value).data
else:
fields[cfv.field.name] = cfv.value
return fields
class CustomFieldChoiceSerializer(serializers.ModelSerializer):
class Meta:
model = CustomFieldChoice
fields = ['id', 'value']
class GraphSerializer(serializers.ModelSerializer):

View File

@ -1,9 +1,8 @@
import graphviz
from rest_framework import generics
from rest_framework.views import APIView
import tempfile
from wsgiref.util import FileWrapper
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404
@ -15,6 +14,17 @@ from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_P
from .serializers import GraphSerializer
class CustomFieldModelAPIView(object):
"""
Include the applicable set of CustomField in the view context.
"""
def __init__(self):
super(CustomFieldModelAPIView, self).__init__()
self.content_type = ContentType.objects.get_for_model(self.queryset.model)
self.custom_fields = self.content_type.custom_fields.all()
class GraphListView(generics.ListAPIView):
"""
Returns a list of relevant graphs

View File

@ -1,6 +1,7 @@
from rest_framework import serializers
from dcim.api.serializers import SiteNestedSerializer, InterfaceNestedSerializer
from extras.api.serializers import CustomFieldsSerializer
from ipam.models import VRF, Role, RIR, Aggregate, Prefix, IPAddress, VLAN, VLANGroup
from tenancy.api.serializers import TenantNestedSerializer
@ -9,12 +10,12 @@ from tenancy.api.serializers import TenantNestedSerializer
# VRFs
#
class VRFSerializer(serializers.ModelSerializer):
class VRFSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
tenant = TenantNestedSerializer()
class Meta:
model = VRF
fields = ['id', 'name', 'rd', 'tenant', 'enforce_unique', 'description']
fields = ['id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'custom_fields']
class VRFNestedSerializer(VRFSerializer):
@ -70,12 +71,12 @@ class RIRNestedSerializer(RIRSerializer):
# Aggregates
#
class AggregateSerializer(serializers.ModelSerializer):
class AggregateSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
rir = RIRNestedSerializer()
class Meta:
model = Aggregate
fields = ['id', 'family', 'prefix', 'rir', 'date_added', 'description']
fields = ['id', 'family', 'prefix', 'rir', 'date_added', 'description', 'custom_fields']
class AggregateNestedSerializer(AggregateSerializer):
@ -106,7 +107,7 @@ class VLANGroupNestedSerializer(VLANGroupSerializer):
# VLANs
#
class VLANSerializer(serializers.ModelSerializer):
class VLANSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
site = SiteNestedSerializer()
group = VLANGroupNestedSerializer()
tenant = TenantNestedSerializer()
@ -114,7 +115,8 @@ class VLANSerializer(serializers.ModelSerializer):
class Meta:
model = VLAN
fields = ['id', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'display_name']
fields = ['id', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'display_name',
'custom_fields']
class VLANNestedSerializer(VLANSerializer):
@ -127,7 +129,7 @@ class VLANNestedSerializer(VLANSerializer):
# Prefixes
#
class PrefixSerializer(serializers.ModelSerializer):
class PrefixSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
site = SiteNestedSerializer()
vrf = VRFTenantSerializer()
tenant = TenantNestedSerializer()
@ -136,7 +138,8 @@ class PrefixSerializer(serializers.ModelSerializer):
class Meta:
model = Prefix
fields = ['id', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'description']
fields = ['id', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'description',
'custom_fields']
class PrefixNestedSerializer(PrefixSerializer):
@ -149,14 +152,15 @@ class PrefixNestedSerializer(PrefixSerializer):
# IP addresses
#
class IPAddressSerializer(serializers.ModelSerializer):
class IPAddressSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
vrf = VRFTenantSerializer()
tenant = TenantNestedSerializer()
interface = InterfaceNestedSerializer()
class Meta:
model = IPAddress
fields = ['id', 'family', 'address', 'vrf', 'tenant', 'interface', 'description', 'nat_inside', 'nat_outside']
fields = ['id', 'family', 'address', 'vrf', 'tenant', 'interface', 'description', 'nat_inside', 'nat_outside',
'custom_fields']
class IPAddressNestedSerializer(IPAddressSerializer):

View File

@ -3,6 +3,7 @@ from rest_framework import generics
from ipam.models import VRF, Role, RIR, Aggregate, Prefix, IPAddress, VLAN, VLANGroup
from ipam import filters
from extras.api.views import CustomFieldModelAPIView
from . import serializers
@ -10,20 +11,20 @@ from . import serializers
# VRFs
#
class VRFListView(generics.ListAPIView):
class VRFListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List all VRFs
"""
queryset = VRF.objects.select_related('tenant')
queryset = VRF.objects.select_related('tenant').prefetch_related('custom_field_values')
serializer_class = serializers.VRFSerializer
filter_class = filters.VRFFilter
class VRFDetailView(generics.RetrieveAPIView):
class VRFDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single VRF
"""
queryset = VRF.objects.select_related('tenant')
queryset = VRF.objects.select_related('tenant').prefetch_related('custom_field_values')
serializer_class = serializers.VRFSerializer
@ -71,20 +72,20 @@ class RIRDetailView(generics.RetrieveAPIView):
# Aggregates
#
class AggregateListView(generics.ListAPIView):
class AggregateListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List aggregates (filterable)
"""
queryset = Aggregate.objects.select_related('rir')
queryset = Aggregate.objects.select_related('rir').prefetch_related('custom_field_values')
serializer_class = serializers.AggregateSerializer
filter_class = filters.AggregateFilter
class AggregateDetailView(generics.RetrieveAPIView):
class AggregateDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single aggregate
"""
queryset = Aggregate.objects.select_related('rir')
queryset = Aggregate.objects.select_related('rir').prefetch_related('custom_field_values')
serializer_class = serializers.AggregateSerializer
@ -92,20 +93,22 @@ class AggregateDetailView(generics.RetrieveAPIView):
# Prefixes
#
class PrefixListView(generics.ListAPIView):
class PrefixListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List prefixes (filterable)
"""
queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')\
.prefetch_related('custom_field_values')
serializer_class = serializers.PrefixSerializer
filter_class = filters.PrefixFilter
class PrefixDetailView(generics.RetrieveAPIView):
class PrefixDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single prefix
"""
queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')\
.prefetch_related('custom_field_values')
serializer_class = serializers.PrefixSerializer
@ -113,22 +116,22 @@ class PrefixDetailView(generics.RetrieveAPIView):
# IP addresses
#
class IPAddressListView(generics.ListAPIView):
class IPAddressListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List IP addresses (filterable)
"""
queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device', 'nat_inside')\
.prefetch_related('nat_outside')
.prefetch_related('nat_outside', 'custom_field_values')
serializer_class = serializers.IPAddressSerializer
filter_class = filters.IPAddressFilter
class IPAddressDetailView(generics.RetrieveAPIView):
class IPAddressDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single IP address
"""
queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device', 'nat_inside')\
.prefetch_related('nat_outside')
.prefetch_related('nat_outside', 'custom_field_values')
serializer_class = serializers.IPAddressSerializer
@ -157,18 +160,18 @@ class VLANGroupDetailView(generics.RetrieveAPIView):
# VLANs
#
class VLANListView(generics.ListAPIView):
class VLANListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List VLANs (filterable)
"""
queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role').prefetch_related('custom_field_values')
serializer_class = serializers.VLANSerializer
filter_class = filters.VLANFilter
class VLANDetailView(generics.RetrieveAPIView):
class VLANDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single VLAN
"""
queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role').prefetch_related('custom_field_values')
serializer_class = serializers.VLANSerializer

View File

@ -1,6 +1,7 @@
from netaddr import IPNetwork, cidr_merge
from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.core.validators import MaxValueValidator, MinValueValidator
@ -8,7 +9,7 @@ from django.db import models
from django.db.models.expressions import RawSQL
from dcim.models import Interface
from extras.models import CustomFieldModel
from extras.models import CustomFieldModel, CustomFieldValue
from tenancy.models import Tenant
from utilities.models import CreatedUpdatedModel
@ -53,6 +54,7 @@ class VRF(CreatedUpdatedModel, CustomFieldModel):
enforce_unique = models.BooleanField(default=True, verbose_name='Enforce unique space',
help_text="Prevent duplicate prefixes/IP addresses within this VRF")
description = models.CharField(max_length=100, blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
class Meta:
ordering = ['name']
@ -105,6 +107,7 @@ class Aggregate(CreatedUpdatedModel, CustomFieldModel):
rir = models.ForeignKey('RIR', related_name='aggregates', on_delete=models.PROTECT, verbose_name='RIR')
date_added = models.DateField(blank=True, null=True)
description = models.CharField(max_length=100, blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
class Meta:
ordering = ['family', 'prefix']
@ -241,6 +244,7 @@ class Prefix(CreatedUpdatedModel, CustomFieldModel):
status = models.PositiveSmallIntegerField('Status', choices=PREFIX_STATUS_CHOICES, default=1)
role = models.ForeignKey('Role', related_name='prefixes', on_delete=models.SET_NULL, blank=True, null=True)
description = models.CharField(max_length=100, blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
objects = PrefixQuerySet.as_manager()
@ -332,6 +336,7 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
nat_inside = models.OneToOneField('self', related_name='nat_outside', on_delete=models.SET_NULL, blank=True,
null=True, verbose_name='NAT IP (inside)')
description = models.CharField(max_length=100, blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
objects = IPAddressManager()
@ -436,6 +441,7 @@ class VLAN(CreatedUpdatedModel, CustomFieldModel):
status = models.PositiveSmallIntegerField('Status', choices=VLAN_STATUS_CHOICES, default=1)
role = models.ForeignKey('Role', related_name='vlans', on_delete=models.SET_NULL, blank=True, null=True)
description = models.CharField(max_length=100, blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
class Meta:
ordering = ['site', 'group', 'vid']

View File

@ -1,5 +1,6 @@
from rest_framework import serializers
from extras.api.serializers import CustomFieldsSerializer
from tenancy.models import Tenant, TenantGroup
@ -24,12 +25,12 @@ class TenantGroupNestedSerializer(TenantGroupSerializer):
# Tenants
#
class TenantSerializer(serializers.ModelSerializer):
class TenantSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
group = TenantGroupNestedSerializer()
class Meta:
model = Tenant
fields = ['id', 'name', 'slug', 'group', 'comments']
fields = ['id', 'name', 'slug', 'group', 'comments', 'custom_fields']
class TenantNestedSerializer(TenantSerializer):

View File

@ -3,6 +3,7 @@ from rest_framework import generics
from tenancy.models import Tenant, TenantGroup
from tenancy.filters import TenantFilter
from extras.api.views import CustomFieldModelAPIView
from . import serializers
@ -22,18 +23,18 @@ class TenantGroupDetailView(generics.RetrieveAPIView):
serializer_class = serializers.TenantGroupSerializer
class TenantListView(generics.ListAPIView):
class TenantListView(CustomFieldModelAPIView, generics.ListAPIView):
"""
List tenants (filterable)
"""
queryset = Tenant.objects.select_related('group')
queryset = Tenant.objects.select_related('group').prefetch_related('custom_field_values')
serializer_class = serializers.TenantSerializer
filter_class = TenantFilter
class TenantDetailView(generics.RetrieveAPIView):
class TenantDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
"""
Retrieve a single tenant
"""
queryset = Tenant.objects.select_related('group')
queryset = Tenant.objects.select_related('group').prefetch_related('custom_field_values')
serializer_class = serializers.TenantSerializer

View File

@ -1,7 +1,8 @@
from django.contrib.contenttypes.fields import GenericRelation
from django.core.urlresolvers import reverse
from django.db import models
from extras.models import CustomFieldModel
from extras.models import CustomFieldModel, CustomFieldValue
from utilities.models import CreatedUpdatedModel
@ -32,6 +33,7 @@ class Tenant(CreatedUpdatedModel, CustomFieldModel):
group = models.ForeignKey('TenantGroup', related_name='tenants', blank=True, null=True, on_delete=models.SET_NULL)
description = models.CharField(max_length=100, blank=True, help_text="Long-form name (optional)")
comments = models.TextField(blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
class Meta:
ordering = ['group', 'name']