1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Revert "refactor extras registry"

This reverts commit c189895f6c.
This commit is contained in:
John Anderson
2020-03-15 00:24:05 -04:00
parent c189895f6c
commit 6ea15cec6f
6 changed files with 40 additions and 44 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -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