From 6ea15cec6f3917028d8226f5760a08be7c981bc3 Mon Sep 17 00:00:00 2001 From: John Anderson Date: Sun, 15 Mar 2020 00:24:05 -0400 Subject: [PATCH] Revert "refactor extras registry" This reverts commit c189895f6c17bd64de21e419a2f6678342d62477. --- netbox/dcim/models/__init__.py | 4 +-- netbox/extras/api/serializers.py | 6 ++-- netbox/extras/models.py | 12 +++---- netbox/extras/tests/test_api.py | 3 +- netbox/extras/tests/test_filters.py | 5 +-- netbox/extras/utils.py | 54 +++++++++++++---------------- 6 files changed, 40 insertions(+), 44 deletions(-) diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py index 40f022fa4..f702f8dff 100644 --- a/netbox/dcim/models/__init__.py +++ b/netbox/dcim/models/__init__.py @@ -21,7 +21,7 @@ from dcim.constants import * from dcim.fields import ASNField from dcim.elevations import RackElevationSVG from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange, TaggedItem -from extras.utils import extras_features +from extras.utils import extras_functionality from utilities.fields import ColorField, NaturalOrderingField from utilities.models import ChangeLoggedModel from utilities.utils import serialize_object, to_meters @@ -1221,7 +1221,7 @@ class Platform(ChangeLoggedModel): ) -@extras_features(['webhooks', 'custom_fields', 'export_templates', 'custom_links', 'graphs']) +@extras_functionality(['webhooks', 'custom_fields', 'export_templates', 'custom_links', 'graphs']) class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel): """ A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType, diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index 774e183ec..9830cdd51 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -14,7 +14,7 @@ from extras.constants import * from extras.models import ( ConfigContext, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, Tag, ) -from extras.utils import FeatureQuerySet +from extras.utils import FunctionalityQueryset from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer from tenancy.models import Tenant, TenantGroup from users.api.nested_serializers import NestedUserSerializer @@ -33,7 +33,7 @@ from .nested_serializers import * class GraphSerializer(ValidatedModelSerializer): type = ContentTypeField( - queryset=ContentType.objects.filter(FeatureQuerySet('graphs').get_queryset()), + queryset=ContentType.objects.filter(FunctionalityQueryset('graphs').get_queryset()), ) class Meta: @@ -69,7 +69,7 @@ class RenderedGraphSerializer(serializers.ModelSerializer): class ExportTemplateSerializer(ValidatedModelSerializer): content_type = ContentTypeField( - queryset=ContentType.objects.filter(FeatureQuerySet('export_templates').get_queryset()), + queryset=ContentType.objects.filter(Q(FunctionalityQueryset('export_templates').get_queryset())), ) template_language = ChoiceField( choices=TemplateLanguageChoices, diff --git a/netbox/extras/models.py b/netbox/extras/models.py index 21809c35b..47c05667c 100644 --- a/netbox/extras/models.py +++ b/netbox/extras/models.py @@ -22,7 +22,7 @@ from utilities.utils import deepmerge, render_jinja2 from .choices import * from .constants import * from .querysets import ConfigContextQuerySet -from .utils import FeatureQuerySet +from .utils import FunctionalityQueryset __all__ = ( @@ -59,7 +59,7 @@ class Webhook(models.Model): to=ContentType, related_name='webhooks', verbose_name='Object types', - limit_choices_to=FeatureQuerySet('webhooks'), + limit_choices_to=FunctionalityQueryset('webhooks'), help_text="The object(s) to which this Webhook applies." ) name = models.CharField( @@ -224,7 +224,7 @@ class CustomField(models.Model): to=ContentType, related_name='custom_fields', verbose_name='Object(s)', - limit_choices_to=FeatureQuerySet('custom_fields'), + limit_choices_to=FunctionalityQueryset('custom_fields'), help_text='The object(s) to which this field applies.' ) type = models.CharField( @@ -471,7 +471,7 @@ class CustomLink(models.Model): content_type = models.ForeignKey( to=ContentType, on_delete=models.CASCADE, - limit_choices_to=FeatureQuerySet('custom_links') + limit_choices_to=FunctionalityQueryset('custom_links') ) name = models.CharField( max_length=100, @@ -519,7 +519,7 @@ class Graph(models.Model): type = models.ForeignKey( to=ContentType, on_delete=models.CASCADE, - limit_choices_to=FeatureQuerySet('graphs') + limit_choices_to=FunctionalityQueryset('graphs') ) weight = models.PositiveSmallIntegerField( default=1000 @@ -582,7 +582,7 @@ class ExportTemplate(models.Model): content_type = models.ForeignKey( to=ContentType, on_delete=models.CASCADE, - limit_choices_to=FeatureQuerySet('export_templates') + limit_choices_to=FunctionalityQueryset('export_templates') ) name = models.CharField( max_length=100 diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index b52091f62..6871b2654 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -8,6 +8,7 @@ from rest_framework import status from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Platform, Rack, RackGroup, RackRole, Region, Site from extras.api.views import ScriptViewSet from extras.choices import * +from extras.constants import GRAPH_MODELS from extras.models import ConfigContext, Graph, ExportTemplate, Tag from extras.scripts import BooleanVar, IntegerVar, Script, StringVar from tenancy.models import Tenant, TenantGroup @@ -34,7 +35,7 @@ class AppTest(APITestCase): self.assertEqual(choices_to_dict(response.data.get('export-template:template_language')), TemplateLanguageChoices.as_dict()) # Graph - content_types = ContentType.objects.filter(FeatureQuerySet('graphs').get_queryset()) + content_types = ContentType.objects.filter(GRAPH_MODELS) graph_type_choices = { "{}.{}".format(ct.app_label, ct.model): str(ct) for ct in content_types } diff --git a/netbox/extras/tests/test_filters.py b/netbox/extras/tests/test_filters.py index 9b02293eb..126414cfd 100644 --- a/netbox/extras/tests/test_filters.py +++ b/netbox/extras/tests/test_filters.py @@ -3,6 +3,7 @@ from django.test import TestCase from dcim.models import DeviceRole, Platform, Region, Site from extras.choices import * +from extras.constants import GRAPH_MODELS from extras.filters import * from extras.models import ConfigContext, ExportTemplate, Graph from tenancy.models import Tenant, TenantGroup @@ -17,7 +18,7 @@ class GraphTestCase(TestCase): def setUpTestData(cls): # Get the first three available types - content_types = ContentType.objects.filter(FeatureQuerySet('graphs').get_queryset())[:3] + content_types = ContentType.objects.filter(GRAPH_MODELS)[:3] graphs = ( Graph(name='Graph 1', type=content_types[0], template_language=TemplateLanguageChoices.LANGUAGE_DJANGO, source='http://example.com/1'), @@ -31,7 +32,7 @@ class GraphTestCase(TestCase): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_type(self): - content_type = ContentType.objects.filter(FeatureQuerySet('graphs').get_queryset()).first() + content_type = ContentType.objects.filter(GRAPH_MODELS).first() params = {'type': content_type.pk} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) diff --git a/netbox/extras/utils.py b/netbox/extras/utils.py index 31a312333..d6e55e6f6 100644 --- a/netbox/extras/utils.py +++ b/netbox/extras/utils.py @@ -22,61 +22,55 @@ def is_taggable(obj): class Registry: """ - The registry is a place to hook into for data storage across components + Singleton object used to store important data """ + instance = None - def add_store(self, store_name, initial_value=None): - """ - Given the name of some new data parameter and an optional initial value, setup the registry store - """ - if not hasattr(Registry, store_name): - setattr(Registry, store_name, initial_value) - -registry = Registry() + def __new__(cls): + if cls.instance is not None: + return cls.instance + else: + cls.instance = super().__new__(cls) + cls.model_functionality_store = {f: collections.defaultdict(list) for f in EXTRAS_FUNCTIONALITIES} + return cls.instance -# -# Dynamic feature registration -# - -class FeatureQuerySet: +class FunctionalityQueryset: """ Helper class that delays evaluation of the registry contents for the functionaility store until it has been populated. """ - def __init__(self, feature): - self.feature = feature + def __init__(self, functionality): + self.functionality = functionality def __call__(self): return self.get_queryset() def get_queryset(self): """ - Given an extras feature, return a Q object for content type lookup + Given an extras functionality, return a Q object for content type lookup """ query = Q() - #registry = Registry() - for app_label, models in registry.model_feature_store[self.feature].items(): + registry = Registry() + for app_label, models in registry.model_functionality_store[self.functionality].items(): query |= Q(app_label=app_label, model__in=models) return query -registry.add_store('model_feature_store', {f: collections.defaultdict(list) for f in EXTRAS_FUNCTIONALITIES}) - - -def extras_features(features): +def extras_functionality(functionalities): """ - Decorator used to register extras provided features to a model + Decorator used to register extras provided functionalities to a model """ def wrapper(model_class): - if isinstance(features, list) and features: - #registry = Registry() - model_class._extras_feature = [] - for feature in features: - if feature in EXTRAS_FUNCTIONALITIES: + if isinstance(functionalities, list) and functionalities: + registry = Registry() + model_class._extras_functionality = [] + for functionality in functionalities: + if functionality in EXTRAS_FUNCTIONALITIES: + model_class._extras_functionality.append(functionality) app_label, model_name = model_class._meta.label_lower.split('.') - registry.model_feature_store[feature][app_label].append(model_name) + registry.model_functionality_store[functionality][app_label].append(model_name) return model_class return wrapper