mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-25 01:48:38 -06:00
Extended API to include custom fields
This commit is contained in:
parent
b14afaa687
commit
76f0463290
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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']
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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']
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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']
|
||||||
|
Loading…
Reference in New Issue
Block a user