Optimized API performance

This commit is contained in:
Jeremy Stretch 2016-08-22 15:16:49 -04:00
parent 76f0463290
commit f0a85b1dd3
11 changed files with 67 additions and 43 deletions

View File

@ -2,7 +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 extras.api.serializers import CustomFieldSerializer
from tenancy.api.serializers import TenantNestedSerializer from tenancy.api.serializers import TenantNestedSerializer
@ -10,7 +10,7 @@ from tenancy.api.serializers import TenantNestedSerializer
# Providers # Providers
# #
class ProviderSerializer(CustomFieldsSerializer, serializers.ModelSerializer): class ProviderSerializer(CustomFieldSerializer, serializers.ModelSerializer):
class Meta: class Meta:
model = Provider model = Provider
@ -45,7 +45,7 @@ class CircuitTypeNestedSerializer(CircuitTypeSerializer):
# Circuits # Circuits
# #
class CircuitSerializer(CustomFieldsSerializer, serializers.ModelSerializer): class CircuitSerializer(CustomFieldSerializer, serializers.ModelSerializer):
provider = ProviderNestedSerializer() provider = ProviderNestedSerializer()
type = CircuitTypeNestedSerializer() type = CircuitTypeNestedSerializer()
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()

View File

@ -11,7 +11,7 @@ class ProviderListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List all providers List all providers
""" """
queryset = Provider.objects.prefetch_related('custom_field_values') queryset = Provider.objects.prefetch_related('custom_field_values__field')
serializer_class = serializers.ProviderSerializer serializer_class = serializers.ProviderSerializer
@ -19,7 +19,7 @@ class ProviderDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single provider Retrieve a single provider
""" """
queryset = Provider.objects.prefetch_related('custom_field_values') queryset = Provider.objects.prefetch_related('custom_field_values__field')
serializer_class = serializers.ProviderSerializer serializer_class = serializers.ProviderSerializer
@ -44,7 +44,7 @@ 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') .prefetch_related('custom_field_values__field')
serializer_class = serializers.CircuitSerializer serializer_class = serializers.CircuitSerializer
filter_class = CircuitFilter filter_class = CircuitFilter
@ -54,5 +54,5 @@ 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') .prefetch_related('custom_field_values__field')
serializer_class = serializers.CircuitSerializer serializer_class = serializers.CircuitSerializer

View File

@ -6,7 +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 extras.api.serializers import CustomFieldSerializer
from tenancy.api.serializers import TenantNestedSerializer from tenancy.api.serializers import TenantNestedSerializer
@ -14,7 +14,7 @@ from tenancy.api.serializers import TenantNestedSerializer
# Sites # Sites
# #
class SiteSerializer(CustomFieldsSerializer, serializers.ModelSerializer): class SiteSerializer(CustomFieldSerializer, serializers.ModelSerializer):
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()
class Meta: class Meta:
@ -69,7 +69,7 @@ class RackRoleNestedSerializer(RackRoleSerializer):
# #
class RackSerializer(CustomFieldsSerializer, serializers.ModelSerializer): class RackSerializer(CustomFieldSerializer, serializers.ModelSerializer):
site = SiteNestedSerializer() site = SiteNestedSerializer()
group = RackGroupNestedSerializer() group = RackGroupNestedSerializer()
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()
@ -238,7 +238,7 @@ class DeviceIPAddressNestedSerializer(serializers.ModelSerializer):
fields = ['id', 'family', 'address'] fields = ['id', 'family', 'address']
class DeviceSerializer(CustomFieldsSerializer, serializers.ModelSerializer): class DeviceSerializer(CustomFieldSerializer, serializers.ModelSerializer):
device_type = DeviceTypeNestedSerializer() device_type = DeviceTypeNestedSerializer()
device_role = DeviceRoleNestedSerializer() device_role = DeviceRoleNestedSerializer()
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()

View File

@ -28,7 +28,7 @@ class SiteListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List all sites List all sites
""" """
queryset = Site.objects.select_related('tenant').prefetch_related('custom_field_values') queryset = Site.objects.select_related('tenant').prefetch_related('custom_field_values__field')
serializer_class = serializers.SiteSerializer serializer_class = serializers.SiteSerializer
@ -36,7 +36,7 @@ class SiteDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single site Retrieve a single site
""" """
queryset = Site.objects.select_related('tenant').prefetch_related('custom_field_values') queryset = Site.objects.select_related('tenant').prefetch_related('custom_field_values__field')
serializer_class = serializers.SiteSerializer serializer_class = serializers.SiteSerializer
@ -89,7 +89,8 @@ class RackListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List racks (filterable) List racks (filterable)
""" """
queryset = Rack.objects.select_related('site', 'group__site', 'tenant').prefetch_related('custom_field_values') queryset = Rack.objects.select_related('site', 'group__site', 'tenant')\
.prefetch_related('custom_field_values__field')
serializer_class = serializers.RackSerializer serializer_class = serializers.RackSerializer
filter_class = filters.RackFilter filter_class = filters.RackFilter
@ -98,7 +99,8 @@ class RackDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single rack Retrieve a single rack
""" """
queryset = Rack.objects.select_related('site', 'group__site', 'tenant').prefetch_related('custom_field_values') queryset = Rack.objects.select_related('site', 'group__site', 'tenant')\
.prefetch_related('custom_field_values__field')
serializer_class = serializers.RackDetailSerializer serializer_class = serializers.RackDetailSerializer
@ -217,7 +219,7 @@ class DeviceListView(CustomFieldModelAPIView, generics.ListAPIView):
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') 'custom_field_values__field')
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]
@ -228,7 +230,7 @@ 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').prefetch_related('custom_field_values') 'rack__site', 'parent_bay').prefetch_related('custom_field_values__field')
serializer_class = serializers.DeviceSerializer serializer_class = serializers.DeviceSerializer

View File

@ -1,21 +1,33 @@
from rest_framework import serializers from rest_framework import serializers
from extras.models import CF_TYPE_SELECT, CustomFieldChoice, Graph from extras.models import CF_TYPE_SELECT, CustomFieldChoice, CustomFieldValue, Graph
class CustomFieldsSerializer(serializers.Serializer): class CustomFieldSerializer(serializers.Serializer):
""" """
Extends a ModelSerializer to render any CustomFields and their values associated with an object. Extends a ModelSerializer to render any CustomFields and their values associated with an object.
""" """
custom_fields = serializers.SerializerMethodField() custom_fields = serializers.SerializerMethodField()
def get_custom_fields(self, obj): def get_custom_fields(self, obj):
# Gather all CustomFields applicable to this object
fields = {cf.name: None for cf in self.context['view'].custom_fields} fields = {cf.name: None for cf in self.context['view'].custom_fields}
# Attach any defined CustomFieldValues to their respective CustomFields
for cfv in obj.custom_field_values.all(): for cfv in obj.custom_field_values.all():
# Suppress database lookups for CustomFieldChoices. Instead, use the cached choice set from the view
# context.
if cfv.field.type == CF_TYPE_SELECT: if cfv.field.type == CF_TYPE_SELECT:
fields[cfv.field.name] = CustomFieldChoiceSerializer(instance=cfv.value).data cfc = {
'id': int(cfv.serialized_value),
'value': self.context['view'].custom_field_choices[int(cfv.serialized_value)]
}
fields[cfv.field.name] = CustomFieldChoiceSerializer(instance=cfc).data
else: else:
fields[cfv.field.name] = cfv.value fields[cfv.field.name] = cfv.value
return fields return fields

View File

@ -9,7 +9,7 @@ from django.shortcuts import get_object_or_404
from circuits.models import Provider from circuits.models import Provider
from dcim.models import Site, Device, Interface, InterfaceConnection from dcim.models import Site, Device, Interface, InterfaceConnection
from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_PROVIDER, GRAPH_TYPE_SITE from extras.models import CustomFieldChoice, Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_PROVIDER, GRAPH_TYPE_SITE
from .serializers import GraphSerializer from .serializers import GraphSerializer
@ -22,7 +22,14 @@ class CustomFieldModelAPIView(object):
def __init__(self): def __init__(self):
super(CustomFieldModelAPIView, self).__init__() super(CustomFieldModelAPIView, self).__init__()
self.content_type = ContentType.objects.get_for_model(self.queryset.model) self.content_type = ContentType.objects.get_for_model(self.queryset.model)
self.custom_fields = self.content_type.custom_fields.all() self.custom_fields = self.content_type.custom_fields.prefetch_related('choices')
# Cache all relevant CustomFieldChoices. This saves us from having to do a lookup per select field per object.
custom_field_choices = {}
for field in self.custom_fields:
for cfc in field.choices.all():
custom_field_choices[cfc.id] = cfc.value
self.custom_field_choices = custom_field_choices
class GraphListView(generics.ListAPIView): class GraphListView(generics.ListAPIView):

View File

@ -129,7 +129,8 @@ class CustomField(models.Model):
# Read date as YYYY-MM-DD # Read date as YYYY-MM-DD
return date(*[int(n) for n in serialized_value.split('-')]) return date(*[int(n) for n in serialized_value.split('-')])
if self.type == CF_TYPE_SELECT: if self.type == CF_TYPE_SELECT:
return CustomFieldChoice.objects.get(pk=int(serialized_value)) # return CustomFieldChoice.objects.get(pk=int(serialized_value))
return self.choices.get(pk=int(serialized_value))
return serialized_value return serialized_value

View File

@ -1,7 +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 extras.api.serializers import CustomFieldSerializer
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
@ -10,7 +10,7 @@ from tenancy.api.serializers import TenantNestedSerializer
# VRFs # VRFs
# #
class VRFSerializer(CustomFieldsSerializer, serializers.ModelSerializer): class VRFSerializer(CustomFieldSerializer, serializers.ModelSerializer):
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()
class Meta: class Meta:
@ -71,7 +71,7 @@ class RIRNestedSerializer(RIRSerializer):
# Aggregates # Aggregates
# #
class AggregateSerializer(CustomFieldsSerializer, serializers.ModelSerializer): class AggregateSerializer(CustomFieldSerializer, serializers.ModelSerializer):
rir = RIRNestedSerializer() rir = RIRNestedSerializer()
class Meta: class Meta:
@ -107,7 +107,7 @@ class VLANGroupNestedSerializer(VLANGroupSerializer):
# VLANs # VLANs
# #
class VLANSerializer(CustomFieldsSerializer, serializers.ModelSerializer): class VLANSerializer(CustomFieldSerializer, serializers.ModelSerializer):
site = SiteNestedSerializer() site = SiteNestedSerializer()
group = VLANGroupNestedSerializer() group = VLANGroupNestedSerializer()
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()
@ -129,7 +129,7 @@ class VLANNestedSerializer(VLANSerializer):
# Prefixes # Prefixes
# #
class PrefixSerializer(CustomFieldsSerializer, serializers.ModelSerializer): class PrefixSerializer(CustomFieldSerializer, serializers.ModelSerializer):
site = SiteNestedSerializer() site = SiteNestedSerializer()
vrf = VRFTenantSerializer() vrf = VRFTenantSerializer()
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()
@ -152,7 +152,7 @@ class PrefixNestedSerializer(PrefixSerializer):
# IP addresses # IP addresses
# #
class IPAddressSerializer(CustomFieldsSerializer, serializers.ModelSerializer): class IPAddressSerializer(CustomFieldSerializer, serializers.ModelSerializer):
vrf = VRFTenantSerializer() vrf = VRFTenantSerializer()
tenant = TenantNestedSerializer() tenant = TenantNestedSerializer()
interface = InterfaceNestedSerializer() interface = InterfaceNestedSerializer()

View File

@ -15,7 +15,7 @@ class VRFListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List all VRFs List all VRFs
""" """
queryset = VRF.objects.select_related('tenant').prefetch_related('custom_field_values') queryset = VRF.objects.select_related('tenant').prefetch_related('custom_field_values__field')
serializer_class = serializers.VRFSerializer serializer_class = serializers.VRFSerializer
filter_class = filters.VRFFilter filter_class = filters.VRFFilter
@ -24,7 +24,7 @@ class VRFDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single VRF Retrieve a single VRF
""" """
queryset = VRF.objects.select_related('tenant').prefetch_related('custom_field_values') queryset = VRF.objects.select_related('tenant').prefetch_related('custom_field_values__field')
serializer_class = serializers.VRFSerializer serializer_class = serializers.VRFSerializer
@ -76,7 +76,7 @@ class AggregateListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List aggregates (filterable) List aggregates (filterable)
""" """
queryset = Aggregate.objects.select_related('rir').prefetch_related('custom_field_values') queryset = Aggregate.objects.select_related('rir').prefetch_related('custom_field_values__field')
serializer_class = serializers.AggregateSerializer serializer_class = serializers.AggregateSerializer
filter_class = filters.AggregateFilter filter_class = filters.AggregateFilter
@ -85,7 +85,7 @@ class AggregateDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single aggregate Retrieve a single aggregate
""" """
queryset = Aggregate.objects.select_related('rir').prefetch_related('custom_field_values') queryset = Aggregate.objects.select_related('rir').prefetch_related('custom_field_values__field')
serializer_class = serializers.AggregateSerializer serializer_class = serializers.AggregateSerializer
@ -98,7 +98,7 @@ 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') .prefetch_related('custom_field_values__field')
serializer_class = serializers.PrefixSerializer serializer_class = serializers.PrefixSerializer
filter_class = filters.PrefixFilter filter_class = filters.PrefixFilter
@ -108,7 +108,7 @@ 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') .prefetch_related('custom_field_values__field')
serializer_class = serializers.PrefixSerializer serializer_class = serializers.PrefixSerializer
@ -121,7 +121,7 @@ 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', 'custom_field_values') .prefetch_related('nat_outside', 'custom_field_values__field')
serializer_class = serializers.IPAddressSerializer serializer_class = serializers.IPAddressSerializer
filter_class = filters.IPAddressFilter filter_class = filters.IPAddressFilter
@ -131,7 +131,7 @@ 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', 'custom_field_values') .prefetch_related('nat_outside', 'custom_field_values__field')
serializer_class = serializers.IPAddressSerializer serializer_class = serializers.IPAddressSerializer
@ -164,7 +164,8 @@ class VLANListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List VLANs (filterable) List VLANs (filterable)
""" """
queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role').prefetch_related('custom_field_values') queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')\
.prefetch_related('custom_field_values__field')
serializer_class = serializers.VLANSerializer serializer_class = serializers.VLANSerializer
filter_class = filters.VLANFilter filter_class = filters.VLANFilter
@ -173,5 +174,6 @@ class VLANDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single VLAN Retrieve a single VLAN
""" """
queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role').prefetch_related('custom_field_values') queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')\
.prefetch_related('custom_field_values__field')
serializer_class = serializers.VLANSerializer serializer_class = serializers.VLANSerializer

View File

@ -1,6 +1,6 @@
from rest_framework import serializers from rest_framework import serializers
from extras.api.serializers import CustomFieldsSerializer from extras.api.serializers import CustomFieldSerializer
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
@ -25,7 +25,7 @@ class TenantGroupNestedSerializer(TenantGroupSerializer):
# Tenants # Tenants
# #
class TenantSerializer(CustomFieldsSerializer, serializers.ModelSerializer): class TenantSerializer(CustomFieldSerializer, serializers.ModelSerializer):
group = TenantGroupNestedSerializer() group = TenantGroupNestedSerializer()
class Meta: class Meta:

View File

@ -27,7 +27,7 @@ class TenantListView(CustomFieldModelAPIView, generics.ListAPIView):
""" """
List tenants (filterable) List tenants (filterable)
""" """
queryset = Tenant.objects.select_related('group').prefetch_related('custom_field_values') queryset = Tenant.objects.select_related('group').prefetch_related('custom_field_values__field')
serializer_class = serializers.TenantSerializer serializer_class = serializers.TenantSerializer
filter_class = TenantFilter filter_class = TenantFilter
@ -36,5 +36,5 @@ class TenantDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
""" """
Retrieve a single tenant Retrieve a single tenant
""" """
queryset = Tenant.objects.select_related('group').prefetch_related('custom_field_values') queryset = Tenant.objects.select_related('group').prefetch_related('custom_field_values__field')
serializer_class = serializers.TenantSerializer serializer_class = serializers.TenantSerializer