Merge pull request #3069 from digitalocean/2647-caching

intial work on #2647 - caching
This commit is contained in:
Jeremy Stretch 2019-04-15 17:16:10 -04:00 committed by GitHub
commit 1877afc760
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 148 additions and 11 deletions

View File

@ -1,6 +1,7 @@
sudo: required
services:
- postgresql
- redis-server
addons:
postgresql: "9.4"
language: python

View File

@ -18,6 +18,10 @@ django-filter
# https://github.com/django-mptt/django-mptt
django-mptt
# Django caching using Redis
# https://github.com/niwinz/django-redis
django-redis
# Abstraction models for rendering and paginating HTML tables
# https://github.com/jieter/django-tables2
django-tables2

View File

@ -1,9 +1,12 @@
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db import transaction
from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View
from extras.models import Graph, GRAPH_TYPE_PROVIDER
@ -32,6 +35,7 @@ class ProviderListView(PermissionRequiredMixin, ObjectListView):
class ProviderView(PermissionRequiredMixin, View):
permission_required = 'circuits.view_provider'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, slug):
provider = get_object_or_404(Provider, slug=slug)
@ -147,6 +151,7 @@ class CircuitListView(PermissionRequiredMixin, ObjectListView):
class CircuitView(PermissionRequiredMixin, View):
permission_required = 'circuits.view_circuit'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
circuit = get_object_or_404(Circuit.objects.select_related('provider', 'type', 'tenant__group'), pk=pk)

View File

@ -13,6 +13,8 @@ from django.urls import reverse
from django.utils.html import escape
from django.utils.http import is_safe_url
from django.utils.safestring import mark_safe
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View
from circuits.models import Circuit
@ -195,6 +197,7 @@ class SiteListView(PermissionRequiredMixin, ObjectListView):
class SiteView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_site'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, slug):
site = get_object_or_404(Site.objects.select_related('region', 'tenant__group'), slug=slug)
@ -353,6 +356,7 @@ class RackElevationListView(PermissionRequiredMixin, View):
"""
permission_required = 'dcim.view_rack'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request):
racks = Rack.objects.select_related(
@ -392,6 +396,7 @@ class RackElevationListView(PermissionRequiredMixin, View):
class RackView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_rack'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
rack = get_object_or_404(Rack.objects.select_related('site__region', 'tenant__group', 'group', 'role'), pk=pk)
@ -570,6 +575,7 @@ class DeviceTypeListView(PermissionRequiredMixin, ObjectListView):
class DeviceTypeView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_devicetype'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
devicetype = get_object_or_404(DeviceType, pk=pk)
@ -910,6 +916,7 @@ class DeviceListView(PermissionRequiredMixin, ObjectListView):
class DeviceView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_device'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
device = get_object_or_404(Device.objects.select_related(
@ -991,6 +998,7 @@ class DeviceView(PermissionRequiredMixin, View):
class DeviceInventoryView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_device'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
device = get_object_or_404(Device, pk=pk)
@ -1012,6 +1020,7 @@ class DeviceInventoryView(PermissionRequiredMixin, View):
class DeviceStatusView(PermissionRequiredMixin, View):
permission_required = ('dcim.view_device', 'dcim.napalm_read')
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
device = get_object_or_404(Device, pk=pk)
@ -1025,6 +1034,7 @@ class DeviceStatusView(PermissionRequiredMixin, View):
class DeviceLLDPNeighborsView(PermissionRequiredMixin, View):
permission_required = ('dcim.view_device', 'dcim.napalm_read')
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
device = get_object_or_404(Device, pk=pk)
@ -1042,6 +1052,7 @@ class DeviceLLDPNeighborsView(PermissionRequiredMixin, View):
class DeviceConfigView(PermissionRequiredMixin, View):
permission_required = ('dcim.view_device', 'dcim.napalm_read')
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
device = get_object_or_404(Device, pk=pk)
@ -1279,6 +1290,7 @@ class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
class InterfaceView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_interface'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
interface = get_object_or_404(Interface, pk=pk)
@ -1499,6 +1511,7 @@ class DeviceBayDeleteView(PermissionRequiredMixin, ObjectDeleteView):
class DeviceBayPopulateView(PermissionRequiredMixin, View):
permission_required = 'dcim.change_devicebay'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
device_bay = get_object_or_404(DeviceBay, pk=pk)
@ -1533,6 +1546,7 @@ class DeviceBayPopulateView(PermissionRequiredMixin, View):
class DeviceBayDepopulateView(PermissionRequiredMixin, View):
permission_required = 'dcim.change_devicebay'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
device_bay = get_object_or_404(DeviceBay, pk=pk)
@ -1672,6 +1686,7 @@ class CableListView(PermissionRequiredMixin, ObjectListView):
class CableView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_cable'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
cable = get_object_or_404(Cable, pk=pk)
@ -1687,6 +1702,7 @@ class CableTraceView(PermissionRequiredMixin, View):
"""
permission_required = 'dcim.view_cable'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, model, pk):
obj = get_object_or_404(model, pk=pk)
@ -1726,6 +1742,7 @@ class CableCreateView(PermissionRequiredMixin, GetReturnURLMixin, View):
return super().dispatch(request, *args, **kwargs)
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, *args, **kwargs):
# Parse initial data manually to avoid setting field values as lists
@ -2042,6 +2059,7 @@ class VirtualChassisCreateView(PermissionRequiredMixin, View):
class VirtualChassisEditView(PermissionRequiredMixin, GetReturnURLMixin, View):
permission_required = 'dcim.change_virtualchassis'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
virtual_chassis = get_object_or_404(VirtualChassis, pk=pk)
@ -2110,6 +2128,7 @@ class VirtualChassisDeleteView(PermissionRequiredMixin, ObjectDeleteView):
class VirtualChassisAddMemberView(PermissionRequiredMixin, GetReturnURLMixin, View):
permission_required = 'dcim.change_virtualchassis'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
virtual_chassis = get_object_or_404(VirtualChassis, pk=pk)
@ -2164,6 +2183,7 @@ class VirtualChassisAddMemberView(PermissionRequiredMixin, GetReturnURLMixin, Vi
class VirtualChassisRemoveMemberView(PermissionRequiredMixin, GetReturnURLMixin, View):
permission_required = 'dcim.change_virtualchassis'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
device = get_object_or_404(Device, pk=pk, virtual_chassis__isnull=False)
@ -2227,6 +2247,7 @@ class PowerPanelListView(PermissionRequiredMixin, ObjectListView):
class PowerPanelView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_powerpanel'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
powerpanel = get_object_or_404(PowerPanel.objects.select_related('site', 'rack_group'), pk=pk)
@ -2296,6 +2317,7 @@ class PowerFeedListView(PermissionRequiredMixin, ObjectListView):
class PowerFeedView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_powerfeed'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
powerfeed = get_object_or_404(PowerFeed.objects.select_related('power_panel', 'rack'), pk=pk)

View File

@ -7,6 +7,8 @@ from django.db.models import Count, Q
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.safestring import mark_safe
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View
from django_tables2 import RequestConfig
@ -41,6 +43,7 @@ class TagListView(ObjectListView):
class TagView(View):
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, slug):
tag = get_object_or_404(Tag, slug=slug)
@ -108,6 +111,7 @@ class ConfigContextListView(PermissionRequiredMixin, ObjectListView):
class ConfigContextView(PermissionRequiredMixin, View):
permission_required = 'extras.view_configcontext'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
configcontext = get_object_or_404(ConfigContext, pk=pk)
@ -155,6 +159,7 @@ class ObjectConfigContextView(View):
object_class = None
base_template = None
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
obj = get_object_or_404(self.object_class, pk=pk)
@ -187,6 +192,7 @@ class ObjectChangeListView(PermissionRequiredMixin, ObjectListView):
class ObjectChangeView(PermissionRequiredMixin, View):
permission_required = 'extras.view_objectchange'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
objectchange = get_object_or_404(ObjectChange, pk=pk)
@ -209,6 +215,7 @@ class ObjectChangeLogView(View):
Present a history of changes made to a particular object.
"""
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, model, **kwargs):
# Get object my model and kwargs (e.g. slug='foo')
@ -282,6 +289,7 @@ class ReportListView(PermissionRequiredMixin, View):
"""
permission_required = 'extras.view_reportresult'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request):
reports = get_reports()
@ -306,6 +314,7 @@ class ReportView(PermissionRequiredMixin, View):
"""
permission_required = 'extras.view_reportresult'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, name):
# Retrieve the Report by "<module>.<report>"

View File

@ -3,6 +3,8 @@ from django.conf import settings
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Count, Q
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View
from django_tables2 import RequestConfig
@ -125,6 +127,7 @@ class VRFListView(PermissionRequiredMixin, ObjectListView):
class VRFView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vrf'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
vrf = get_object_or_404(VRF.objects.all(), pk=pk)
@ -319,6 +322,7 @@ class AggregateListView(PermissionRequiredMixin, ObjectListView):
class AggregateView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_aggregate'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
aggregate = get_object_or_404(Aggregate, pk=pk)
@ -456,6 +460,7 @@ class PrefixListView(PermissionRequiredMixin, ObjectListView):
class PrefixView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_prefix'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
prefix = get_object_or_404(Prefix.objects.select_related(
@ -500,6 +505,7 @@ class PrefixView(PermissionRequiredMixin, View):
class PrefixPrefixesView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_prefix'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
prefix = get_object_or_404(Prefix.objects.all(), pk=pk)
@ -543,6 +549,7 @@ class PrefixPrefixesView(PermissionRequiredMixin, View):
class PrefixIPAddressesView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_prefix'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
prefix = get_object_or_404(Prefix.objects.all(), pk=pk)
@ -643,6 +650,7 @@ class IPAddressListView(PermissionRequiredMixin, ObjectListView):
class IPAddressView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_ipaddress'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
ipaddress = get_object_or_404(IPAddress.objects.select_related('vrf__tenant', 'tenant'), pk=pk)
@ -726,6 +734,7 @@ class IPAddressAssignView(PermissionRequiredMixin, View):
return super().dispatch(request, *args, **kwargs)
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request):
form = forms.IPAddressAssignForm()
@ -838,6 +847,7 @@ class VLANGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
class VLANGroupVLANsView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vlangroup'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
vlan_group = get_object_or_404(VLANGroup.objects.all(), pk=pk)
@ -888,6 +898,7 @@ class VLANListView(PermissionRequiredMixin, ObjectListView):
class VLANView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vlan'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
vlan = get_object_or_404(VLAN.objects.select_related(
@ -906,6 +917,7 @@ class VLANView(PermissionRequiredMixin, View):
class VLANMembersView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vlan'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
vlan = get_object_or_404(VLAN.objects.all(), pk=pk)
@ -984,6 +996,7 @@ class ServiceListView(PermissionRequiredMixin, ObjectListView):
class ServiceView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_service'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
service = get_object_or_404(Service, pk=pk)

View File

@ -25,6 +25,16 @@ DATABASE = {
# https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-SECRET_KEY
SECRET_KEY = ''
# Redis database settings. The Redis database is used for caching and background processing such as webhooks
REDIS = {
'HOST': 'localhost',
'PORT': 6379,
'PASSWORD': '',
'DATABASE': 0,
'DEFAULT_TIMEOUT': 300,
'SSL': False,
}
#########################
# #
@ -50,6 +60,18 @@ BANNER_LOGIN = ''
# BASE_PATH = 'netbox/'
BASE_PATH = ''
# The fraction of entries that are culled when CACHE_MAX_ENTRIES is reached. The actual ratio is 1 / CACHE_CULL_FREQUENCY,
# so set CACHE_CULL_FREQUENCY to 2 to cull half the entries when CACHE_MAX_ENTRIES is reached. This setting should be an
# integer and defaults to 3
CACHE_CULL_FREQUENCY = 3
# Max number of entries (unique pages) to store in the cache at a time.
CACHE_MAX_ENTRIES = 300
# Cache timeout in seconds. Set to `None` to enforce an infinate timeout. Set to 0 to dissable caching by immediatly
# expiring keys. Defaults to 900 (15 minutes)
CACHE_TIMEOUT = 900
# Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90)
CHANGELOG_RETENTION = 90
@ -133,16 +155,6 @@ PAGINATE_COUNT = 50
# prefer IPv4 instead.
PREFER_IPV4 = False
# Redis database settings (optional). A Redis database is required only if the webhooks backend is enabled.
REDIS = {
'HOST': 'localhost',
'PORT': 6379,
'PASSWORD': '',
'DATABASE': 0,
'DEFAULT_TIMEOUT': 300,
'SSL': False,
}
# The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of
# this setting is derived from the installed location.
# REPORTS_ROOT = '/opt/netbox/netbox/reports'

View File

@ -28,7 +28,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Import required configuration parameters
ALLOWED_HOSTS = DATABASE = SECRET_KEY = None
for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']:
for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY', 'REDIS']:
try:
globals()[setting] = getattr(configuration, setting)
except AttributeError:
@ -44,6 +44,9 @@ BANNER_TOP = getattr(configuration, 'BANNER_TOP', '')
BASE_PATH = getattr(configuration, 'BASE_PATH', '')
if BASE_PATH:
BASE_PATH = BASE_PATH.strip('/') + '/' # Enforce trailing slash only
CACHE_TIMEOUT = getattr(configuration, 'CACHE_TIMEOUT', 900)
CACHE_MAX_ENTRIES = getattr(configuration, 'CACHE_MAX_ENTRIES', 300)
CACHE_CULL_FREQUENCY = getattr(configuration, 'CACHE_CULL_FREQUENCY', 3)
CHANGELOG_RETENTION = getattr(configuration, 'CHANGELOG_RETENTION', 90)
CORS_ORIGIN_ALLOW_ALL = getattr(configuration, 'CORS_ORIGIN_ALLOW_ALL', False)
CORS_ORIGIN_REGEX_WHITELIST = getattr(configuration, 'CORS_ORIGIN_REGEX_WHITELIST', [])
@ -157,6 +160,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
'django.contrib.humanize',
'corsheaders',
'django_redis',
'debug_toolbar',
'django_filters',
'django_tables2',
@ -218,6 +222,31 @@ TEMPLATES = [
},
]
# Caching
if REDIS_SSL:
REDIS_CACHE_CON_STRING = 'rediss://'
else:
REDIS_CACHE_CON_STRING = 'redis://'
if REDIS_PASSWORD:
REDIS_CACHE_CON_STRING = '{}@{}'.format(REDIS_PASSWORD, REDIS_CACHE_CON_STRING)
REDIS_CACHE_CON_STRING = '{}{}:{}/{}'.format(REDIS_CACHE_CON_STRING, REDIS_HOST, REDIS_PORT, REDIS_DATABASE)
CACHE_BACKEND = 'django_redis.cache.RedisCache'
CACHES = {
"default": {
"BACKEND": CACHE_BACKEND,
"LOCATION": REDIS_CACHE_CON_STRING,
'TIMEOUT': CACHE_TIMEOUT,
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"MAX_ENTRIES": CACHE_MAX_ENTRIES,
"CULL_FREQUENCY": CACHE_CULL_FREQUENCY
}
}
}
# WSGI
WSGI_APPLICATION = 'netbox.wsgi.application'
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

View File

@ -1,7 +1,10 @@
from collections import OrderedDict
from django.conf import settings
from django.db.models import Count, F
from django.shortcuts import render
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View
from rest_framework.response import Response
from rest_framework.reverse import reverse
@ -160,6 +163,7 @@ SEARCH_TYPES = OrderedDict((
class HomeView(View):
template_name = 'home.html'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request):
connected_consoleports = ConsolePort.objects.filter(
@ -219,6 +223,7 @@ class HomeView(View):
class SearchView(View):
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request):
# No query
@ -272,6 +277,7 @@ class APIRootView(APIView):
def get_view_name(self):
return "API Root"
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, format=None):
return Response(OrderedDict((

View File

@ -1,5 +1,6 @@
import base64
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import permission_required, login_required
from django.contrib.auth.mixins import PermissionRequiredMixin
@ -7,6 +8,7 @@ from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View
from dcim.models import Device
@ -80,6 +82,7 @@ class SecretListView(PermissionRequiredMixin, ObjectListView):
class SecretView(PermissionRequiredMixin, View):
permission_required = 'secrets.view_secret'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
secret = get_object_or_404(Secret, pk=pk)

View File

@ -1,6 +1,9 @@
from django.conf import settings
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Count
from django.shortcuts import get_object_or_404, render
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View
from circuits.models import Circuit
@ -66,6 +69,7 @@ class TenantListView(PermissionRequiredMixin, ObjectListView):
class TenantView(PermissionRequiredMixin, View):
permission_required = 'tenancy.view_tenant'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, slug):
tenant = get_object_or_404(Tenant, slug=slug)

View File

@ -6,6 +6,8 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import ManyToManyField
from django.http import Http404
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework.exceptions import APIException
from rest_framework.permissions import BasePermission
from rest_framework.relations import PrimaryKeyRelatedField, RelatedField
@ -248,6 +250,20 @@ class ModelViewSet(_ModelViewSet):
# Fall back to the hard-coded serializer class
return self.serializer_class
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def list(self, *args, **kwargs):
"""
Call to super to allow for caching
"""
return super().list(*args, **kwargs)
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def retrieve(self, *args, **kwargs):
"""
Call to super to allow for caching
"""
return super().retrieve(*args, **kwargs)
class FieldChoicesViewSet(ViewSet):
"""
@ -284,9 +300,11 @@ class FieldChoicesViewSet(ViewSet):
})
self._fields[key] = choices
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def list(self, request):
return Response(self._fields)
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def retrieve(self, request, pk):
if pk not in self._fields:
raise Http404

View File

@ -17,6 +17,8 @@ from django.urls import reverse
from django.utils.html import escape
from django.utils.http import is_safe_url
from django.utils.safestring import mark_safe
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.decorators.csrf import requires_csrf_token
from django.views.defaults import ERROR_500_TEMPLATE_NAME
from django.views.generic import View
@ -106,6 +108,7 @@ class ObjectListView(View):
return csv_data
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request):
model = self.queryset.model
@ -713,6 +716,7 @@ class ComponentCreateView(View):
model_form = None
template_name = None
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
parent = get_object_or_404(self.parent_model, pk=pk)

View File

@ -1,9 +1,12 @@
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db import transaction
from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import View
from dcim.models import Device, Interface
@ -106,6 +109,7 @@ class ClusterListView(PermissionRequiredMixin, ObjectListView):
class ClusterView(PermissionRequiredMixin, View):
permission_required = 'virtualization.view_cluster'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
cluster = get_object_or_404(Cluster, pk=pk)
@ -168,6 +172,7 @@ class ClusterAddDevicesView(PermissionRequiredMixin, View):
form = forms.ClusterAddDevicesForm
template_name = 'virtualization/cluster_add_devices.html'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
cluster = get_object_or_404(Cluster, pk=pk)
@ -263,6 +268,7 @@ class VirtualMachineListView(PermissionRequiredMixin, ObjectListView):
class VirtualMachineView(PermissionRequiredMixin, View):
permission_required = 'virtualization.view_virtualmachine'
@method_decorator(cache_page(settings.CACHE_TIMEOUT))
def get(self, request, pk):
virtualmachine = get_object_or_404(VirtualMachine.objects.select_related('tenant__group'), pk=pk)

View File

@ -3,6 +3,7 @@ django-cors-headers==2.5.2
django-debug-toolbar==1.11
django-filter==2.1.0
django-mptt==0.9.1
django-redis==4.5.0
django-tables2==2.0.6
django-taggit==1.1.0
django-taggit-serializer==0.1.7