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 circuits.models import Provider, CircuitType, Circuit
from dcim.api.serializers import SiteNestedSerializer, InterfaceNestedSerializer from dcim.api.serializers import SiteNestedSerializer, InterfaceNestedSerializer
from extras.api.serializers import CustomFieldsSerializer
from tenancy.api.serializers import TenantNestedSerializer from tenancy.api.serializers import TenantNestedSerializer
@ -9,11 +10,12 @@ from tenancy.api.serializers import TenantNestedSerializer
# Providers # Providers
# #
class ProviderSerializer(serializers.ModelSerializer): class ProviderSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
class Meta: class Meta:
model = Provider 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): class ProviderNestedSerializer(ProviderSerializer):
@ -43,7 +45,7 @@ class CircuitTypeNestedSerializer(CircuitTypeSerializer):
# Circuits # Circuits
# #
class CircuitSerializer(serializers.ModelSerializer): class CircuitSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
provider = ProviderNestedSerializer() provider = ProviderNestedSerializer()
type = CircuitTypeNestedSerializer() type = CircuitTypeNestedSerializer()
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()
@ -53,7 +55,7 @@ class CircuitSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Circuit model = Circuit
fields = ['id', 'cid', 'provider', 'type', 'tenant', 'site', 'interface', 'install_date', 'port_speed', 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): class CircuitNestedSerializer(CircuitSerializer):

View File

@ -3,22 +3,23 @@ from rest_framework import generics
from circuits.models import Provider, CircuitType, Circuit from circuits.models import Provider, CircuitType, Circuit
from circuits.filters import CircuitFilter from circuits.filters import CircuitFilter
from extras.api.views import CustomFieldModelAPIView
from . import serializers from . import serializers
class ProviderListView(generics.ListAPIView): class ProviderListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List all providers List all providers
""" """
queryset = Provider.objects.all() queryset = Provider.objects.prefetch_related('custom_field_values')
serializer_class = serializers.ProviderSerializer serializer_class = serializers.ProviderSerializer
class ProviderDetailView(generics.RetrieveAPIView): class ProviderDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single provider Retrieve a single provider
""" """
queryset = Provider.objects.all() queryset = Provider.objects.prefetch_related('custom_field_values')
serializer_class = serializers.ProviderSerializer serializer_class = serializers.ProviderSerializer
@ -38,18 +39,20 @@ class CircuitTypeDetailView(generics.RetrieveAPIView):
serializer_class = serializers.CircuitTypeSerializer serializer_class = serializers.CircuitTypeSerializer
class CircuitListView(generics.ListAPIView): class CircuitListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List circuits (filterable) 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 serializer_class = serializers.CircuitSerializer
filter_class = CircuitFilter filter_class = CircuitFilter
class CircuitDetailView(generics.RetrieveAPIView): class CircuitDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single circuit 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 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.core.urlresolvers import reverse
from django.db import models from django.db import models
from dcim.fields import ASNField from dcim.fields import ASNField
from dcim.models import Site, Interface from dcim.models import Site, Interface
from extras.models import CustomFieldModel from extras.models import CustomFieldModel, CustomFieldValue
from tenancy.models import Tenant from tenancy.models import Tenant
from utilities.models import CreatedUpdatedModel from utilities.models import CreatedUpdatedModel
@ -21,6 +22,7 @@ class Provider(CreatedUpdatedModel, CustomFieldModel):
noc_contact = models.TextField(blank=True, verbose_name='NOC contact') noc_contact = models.TextField(blank=True, verbose_name='NOC contact')
admin_contact = models.TextField(blank=True, verbose_name='Admin contact') admin_contact = models.TextField(blank=True, verbose_name='Admin contact')
comments = models.TextField(blank=True) comments = models.TextField(blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
@ -79,6 +81,7 @@ class Circuit(CreatedUpdatedModel, CustomFieldModel):
xconnect_id = models.CharField(max_length=50, blank=True, verbose_name='Cross-connect ID') 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)') pp_info = models.CharField(max_length=100, blank=True, verbose_name='Patch panel/port(s)')
comments = models.TextField(blank=True) comments = models.TextField(blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
class Meta: class Meta:
ordering = ['provider', 'cid'] ordering = ['provider', 'cid']

View File

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

View File

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

View File

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

View File

@ -1,6 +1,29 @@
from rest_framework import serializers 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): class GraphSerializer(serializers.ModelSerializer):

View File

@ -1,9 +1,8 @@
import graphviz import graphviz
from rest_framework import generics from rest_framework import generics
from rest_framework.views import APIView 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.db.models import Q
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404 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 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): class GraphListView(generics.ListAPIView):
""" """
Returns a list of relevant graphs Returns a list of relevant graphs

View File

@ -1,6 +1,7 @@
from rest_framework import serializers from rest_framework import serializers
from dcim.api.serializers import SiteNestedSerializer, InterfaceNestedSerializer 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 ipam.models import VRF, Role, RIR, Aggregate, Prefix, IPAddress, VLAN, VLANGroup
from tenancy.api.serializers import TenantNestedSerializer from tenancy.api.serializers import TenantNestedSerializer
@ -9,12 +10,12 @@ from tenancy.api.serializers import TenantNestedSerializer
# VRFs # VRFs
# #
class VRFSerializer(serializers.ModelSerializer): class VRFSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()
class Meta: class Meta:
model = VRF model = VRF
fields = ['id', 'name', 'rd', 'tenant', 'enforce_unique', 'description'] fields = ['id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'custom_fields']
class VRFNestedSerializer(VRFSerializer): class VRFNestedSerializer(VRFSerializer):
@ -70,12 +71,12 @@ class RIRNestedSerializer(RIRSerializer):
# Aggregates # Aggregates
# #
class AggregateSerializer(serializers.ModelSerializer): class AggregateSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
rir = RIRNestedSerializer() rir = RIRNestedSerializer()
class Meta: class Meta:
model = Aggregate model = Aggregate
fields = ['id', 'family', 'prefix', 'rir', 'date_added', 'description'] fields = ['id', 'family', 'prefix', 'rir', 'date_added', 'description', 'custom_fields']
class AggregateNestedSerializer(AggregateSerializer): class AggregateNestedSerializer(AggregateSerializer):
@ -106,7 +107,7 @@ class VLANGroupNestedSerializer(VLANGroupSerializer):
# VLANs # VLANs
# #
class VLANSerializer(serializers.ModelSerializer): class VLANSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
site = SiteNestedSerializer() site = SiteNestedSerializer()
group = VLANGroupNestedSerializer() group = VLANGroupNestedSerializer()
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()
@ -114,7 +115,8 @@ class VLANSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = VLAN 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): class VLANNestedSerializer(VLANSerializer):
@ -127,7 +129,7 @@ class VLANNestedSerializer(VLANSerializer):
# Prefixes # Prefixes
# #
class PrefixSerializer(serializers.ModelSerializer): class PrefixSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
site = SiteNestedSerializer() site = SiteNestedSerializer()
vrf = VRFTenantSerializer() vrf = VRFTenantSerializer()
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()
@ -136,7 +138,8 @@ class PrefixSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Prefix 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): class PrefixNestedSerializer(PrefixSerializer):
@ -149,14 +152,15 @@ class PrefixNestedSerializer(PrefixSerializer):
# IP addresses # IP addresses
# #
class IPAddressSerializer(serializers.ModelSerializer): class IPAddressSerializer(CustomFieldsSerializer, serializers.ModelSerializer):
vrf = VRFTenantSerializer() vrf = VRFTenantSerializer()
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()
interface = InterfaceNestedSerializer() interface = InterfaceNestedSerializer()
class Meta: class Meta:
model = IPAddress 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): 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.models import VRF, Role, RIR, Aggregate, Prefix, IPAddress, VLAN, VLANGroup
from ipam import filters from ipam import filters
from extras.api.views import CustomFieldModelAPIView
from . import serializers from . import serializers
@ -10,20 +11,20 @@ from . import serializers
# VRFs # VRFs
# #
class VRFListView(generics.ListAPIView): class VRFListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List all VRFs List all VRFs
""" """
queryset = VRF.objects.select_related('tenant') queryset = VRF.objects.select_related('tenant').prefetch_related('custom_field_values')
serializer_class = serializers.VRFSerializer serializer_class = serializers.VRFSerializer
filter_class = filters.VRFFilter filter_class = filters.VRFFilter
class VRFDetailView(generics.RetrieveAPIView): class VRFDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single VRF 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 serializer_class = serializers.VRFSerializer
@ -71,20 +72,20 @@ class RIRDetailView(generics.RetrieveAPIView):
# Aggregates # Aggregates
# #
class AggregateListView(generics.ListAPIView): class AggregateListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List aggregates (filterable) List aggregates (filterable)
""" """
queryset = Aggregate.objects.select_related('rir') queryset = Aggregate.objects.select_related('rir').prefetch_related('custom_field_values')
serializer_class = serializers.AggregateSerializer serializer_class = serializers.AggregateSerializer
filter_class = filters.AggregateFilter filter_class = filters.AggregateFilter
class AggregateDetailView(generics.RetrieveAPIView): class AggregateDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single aggregate 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 serializer_class = serializers.AggregateSerializer
@ -92,20 +93,22 @@ class AggregateDetailView(generics.RetrieveAPIView):
# Prefixes # Prefixes
# #
class PrefixListView(generics.ListAPIView): class PrefixListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List prefixes (filterable) 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 serializer_class = serializers.PrefixSerializer
filter_class = filters.PrefixFilter filter_class = filters.PrefixFilter
class PrefixDetailView(generics.RetrieveAPIView): class PrefixDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single prefix 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 serializer_class = serializers.PrefixSerializer
@ -113,22 +116,22 @@ class PrefixDetailView(generics.RetrieveAPIView):
# IP addresses # IP addresses
# #
class IPAddressListView(generics.ListAPIView): class IPAddressListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List IP addresses (filterable) List IP addresses (filterable)
""" """
queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device', 'nat_inside')\ 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 serializer_class = serializers.IPAddressSerializer
filter_class = filters.IPAddressFilter filter_class = filters.IPAddressFilter
class IPAddressDetailView(generics.RetrieveAPIView): class IPAddressDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single IP address Retrieve a single IP address
""" """
queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device', 'nat_inside')\ 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 serializer_class = serializers.IPAddressSerializer
@ -157,18 +160,18 @@ class VLANGroupDetailView(generics.RetrieveAPIView):
# VLANs # VLANs
# #
class VLANListView(generics.ListAPIView): class VLANListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List VLANs (filterable) 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 serializer_class = serializers.VLANSerializer
filter_class = filters.VLANFilter filter_class = filters.VLANFilter
class VLANDetailView(generics.RetrieveAPIView): class VLANDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single VLAN 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 serializer_class = serializers.VLANSerializer

View File

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

View File

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

View File

@ -3,6 +3,7 @@ from rest_framework import generics
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
from tenancy.filters import TenantFilter from tenancy.filters import TenantFilter
from extras.api.views import CustomFieldModelAPIView
from . import serializers from . import serializers
@ -22,18 +23,18 @@ class TenantGroupDetailView(generics.RetrieveAPIView):
serializer_class = serializers.TenantGroupSerializer serializer_class = serializers.TenantGroupSerializer
class TenantListView(generics.ListAPIView): class TenantListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List tenants (filterable) List tenants (filterable)
""" """
queryset = Tenant.objects.select_related('group') queryset = Tenant.objects.select_related('group').prefetch_related('custom_field_values')
serializer_class = serializers.TenantSerializer serializer_class = serializers.TenantSerializer
filter_class = TenantFilter filter_class = TenantFilter
class TenantDetailView(generics.RetrieveAPIView): class TenantDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single tenant 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 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.core.urlresolvers import reverse
from django.db import models from django.db import models
from extras.models import CustomFieldModel from extras.models import CustomFieldModel, CustomFieldValue
from utilities.models import CreatedUpdatedModel 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) 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)") description = models.CharField(max_length=100, blank=True, help_text="Long-form name (optional)")
comments = models.TextField(blank=True) comments = models.TextField(blank=True)
custom_field_values = GenericRelation(CustomFieldValue, content_type_field='obj_type', object_id_field='obj_id')
class Meta: class Meta:
ordering = ['group', 'name'] ordering = ['group', 'name']