mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-29 03:46:25 -06:00
Clean up the application registry
This commit is contained in:
parent
1c2f55668a
commit
f2c9d6e4eb
@ -1,14 +1,2 @@
|
|||||||
# Webhook content types
|
# Webhook content types
|
||||||
HTTP_CONTENT_TYPE_JSON = 'application/json'
|
HTTP_CONTENT_TYPE_JSON = 'application/json'
|
||||||
|
|
||||||
# Registerable extras features
|
|
||||||
EXTRAS_FEATURES = [
|
|
||||||
'custom_fields',
|
|
||||||
'custom_links',
|
|
||||||
'export_templates',
|
|
||||||
'job_results',
|
|
||||||
'journaling',
|
|
||||||
'synced_data',
|
|
||||||
'tags',
|
|
||||||
'webhooks'
|
|
||||||
]
|
|
||||||
|
@ -14,13 +14,13 @@ from .registration import *
|
|||||||
from .templates import *
|
from .templates import *
|
||||||
|
|
||||||
# Initialize plugin registry
|
# Initialize plugin registry
|
||||||
registry['plugins'] = {
|
registry['plugins'].update({
|
||||||
'graphql_schemas': [],
|
'graphql_schemas': [],
|
||||||
'menus': [],
|
'menus': [],
|
||||||
'menu_items': {},
|
'menu_items': {},
|
||||||
'preferences': {},
|
'preferences': {},
|
||||||
'template_extensions': collections.defaultdict(list),
|
'template_extensions': collections.defaultdict(list),
|
||||||
}
|
})
|
||||||
|
|
||||||
DEFAULT_RESOURCE_PATHS = {
|
DEFAULT_RESOURCE_PATHS = {
|
||||||
'search_indexes': 'search.indexes',
|
'search_indexes': 'search.indexes',
|
||||||
|
@ -2,7 +2,6 @@ from django.db.models import Q
|
|||||||
from django.utils.deconstruct import deconstructible
|
from django.utils.deconstruct import deconstructible
|
||||||
from taggit.managers import _TaggableManager
|
from taggit.managers import _TaggableManager
|
||||||
|
|
||||||
from extras.constants import EXTRAS_FEATURES
|
|
||||||
from netbox.registry import registry
|
from netbox.registry import registry
|
||||||
|
|
||||||
|
|
||||||
@ -18,7 +17,7 @@ def is_taggable(obj):
|
|||||||
|
|
||||||
def image_upload(instance, filename):
|
def image_upload(instance, filename):
|
||||||
"""
|
"""
|
||||||
Return a path for uploading image attchments.
|
Return a path for uploading image attachments.
|
||||||
"""
|
"""
|
||||||
path = 'image-attachments/'
|
path = 'image-attachments/'
|
||||||
|
|
||||||
@ -56,8 +55,14 @@ class FeatureQuery:
|
|||||||
|
|
||||||
|
|
||||||
def register_features(model, features):
|
def register_features(model, features):
|
||||||
|
"""
|
||||||
|
Register model features in the application registry.
|
||||||
|
"""
|
||||||
|
app_label, model_name = model._meta.label_lower.split('.')
|
||||||
for feature in features:
|
for feature in features:
|
||||||
if feature not in EXTRAS_FEATURES:
|
try:
|
||||||
raise ValueError(f"{feature} is not a valid extras feature!")
|
registry['model_features'][feature][app_label].add(model_name)
|
||||||
app_label, model_name = model._meta.label_lower.split('.')
|
except KeyError:
|
||||||
registry['model_features'][feature][app_label].add(model_name)
|
raise KeyError(
|
||||||
|
f"{feature} is not a valid model feature! Valid keys are: {registry['model_features'].keys()}"
|
||||||
|
)
|
||||||
|
@ -12,6 +12,7 @@ from taggit.managers import TaggableManager
|
|||||||
|
|
||||||
from extras.choices import CustomFieldVisibilityChoices, ObjectChangeActionChoices
|
from extras.choices import CustomFieldVisibilityChoices, ObjectChangeActionChoices
|
||||||
from extras.utils import is_taggable, register_features
|
from extras.utils import is_taggable, register_features
|
||||||
|
from netbox.registry import registry
|
||||||
from netbox.signals import post_clean
|
from netbox.signals import post_clean
|
||||||
from utilities.json import CustomFieldJSONEncoder
|
from utilities.json import CustomFieldJSONEncoder
|
||||||
from utilities.utils import serialize_object
|
from utilities.utils import serialize_object
|
||||||
@ -388,22 +389,26 @@ class SyncedDataMixin(models.Model):
|
|||||||
raise NotImplementedError(f"{self.__class__} must implement a sync_data() method.")
|
raise NotImplementedError(f"{self.__class__} must implement a sync_data() method.")
|
||||||
|
|
||||||
|
|
||||||
FEATURES_MAP = (
|
FEATURES_MAP = {
|
||||||
('custom_fields', CustomFieldsMixin),
|
'custom_fields': CustomFieldsMixin,
|
||||||
('custom_links', CustomLinksMixin),
|
'custom_links': CustomLinksMixin,
|
||||||
('export_templates', ExportTemplatesMixin),
|
'export_templates': ExportTemplatesMixin,
|
||||||
('job_results', JobResultsMixin),
|
'job_results': JobResultsMixin,
|
||||||
('journaling', JournalingMixin),
|
'journaling': JournalingMixin,
|
||||||
('synced_data', SyncedDataMixin),
|
'synced_data': SyncedDataMixin,
|
||||||
('tags', TagsMixin),
|
'tags': TagsMixin,
|
||||||
('webhooks', WebhooksMixin),
|
'webhooks': WebhooksMixin,
|
||||||
)
|
}
|
||||||
|
|
||||||
|
registry['model_features'].update({
|
||||||
|
feature: defaultdict(set) for feature in FEATURES_MAP.keys()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@receiver(class_prepared)
|
@receiver(class_prepared)
|
||||||
def _register_features(sender, **kwargs):
|
def _register_features(sender, **kwargs):
|
||||||
features = {
|
features = {
|
||||||
feature for feature, cls in FEATURES_MAP if issubclass(sender, cls)
|
feature for feature, cls in FEATURES_MAP.items() if issubclass(sender, cls)
|
||||||
}
|
}
|
||||||
register_features(sender, features)
|
register_features(sender, features)
|
||||||
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import collections
|
import collections
|
||||||
|
|
||||||
from extras.constants import EXTRAS_FEATURES
|
|
||||||
|
|
||||||
|
|
||||||
class Registry(dict):
|
class Registry(dict):
|
||||||
"""
|
"""
|
||||||
Central registry for registration of functionality. Once a store (key) is defined, it cannot be overwritten or
|
Central registry for registration of functionality. Once a Registry is initialized, keys cannot be added or
|
||||||
deleted (although its value may be manipulated).
|
removed (though the value of each key is mutable).
|
||||||
"""
|
"""
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
try:
|
try:
|
||||||
@ -15,20 +13,18 @@ class Registry(dict):
|
|||||||
raise KeyError(f"Invalid store: {key}")
|
raise KeyError(f"Invalid store: {key}")
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
if key in self:
|
raise TypeError("Cannot add stores to registry after initialization")
|
||||||
raise KeyError(f"Store already set: {key}")
|
|
||||||
super().__setitem__(key, value)
|
|
||||||
|
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
raise TypeError("Cannot delete stores from registry")
|
raise TypeError("Cannot delete stores from registry")
|
||||||
|
|
||||||
|
|
||||||
# Initialize the global registry
|
# Initialize the global registry
|
||||||
registry = Registry()
|
registry = Registry({
|
||||||
registry['data_backends'] = dict()
|
'data_backends': dict(),
|
||||||
registry['denormalized_fields'] = collections.defaultdict(list)
|
'denormalized_fields': collections.defaultdict(list),
|
||||||
registry['model_features'] = {
|
'model_features': dict(),
|
||||||
feature: collections.defaultdict(set) for feature in EXTRAS_FEATURES
|
'plugins': dict(),
|
||||||
}
|
'search': dict(),
|
||||||
registry['search'] = dict()
|
'views': collections.defaultdict(dict),
|
||||||
registry['views'] = collections.defaultdict(dict)
|
})
|
||||||
|
@ -5,29 +5,23 @@ from netbox.registry import Registry
|
|||||||
|
|
||||||
class RegistryTest(TestCase):
|
class RegistryTest(TestCase):
|
||||||
|
|
||||||
def test_add_store(self):
|
def test_set_store(self):
|
||||||
reg = Registry()
|
reg = Registry({
|
||||||
reg['foo'] = 123
|
'foo': 123,
|
||||||
|
})
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
reg['bar'] = 456
|
||||||
|
|
||||||
self.assertEqual(reg['foo'], 123)
|
def test_mutate_store(self):
|
||||||
|
reg = Registry({
|
||||||
def test_manipulate_store(self):
|
'foo': [1, 2],
|
||||||
reg = Registry()
|
})
|
||||||
reg['foo'] = [1, 2]
|
|
||||||
reg['foo'].append(3)
|
reg['foo'].append(3)
|
||||||
|
|
||||||
self.assertListEqual(reg['foo'], [1, 2, 3])
|
self.assertListEqual(reg['foo'], [1, 2, 3])
|
||||||
|
|
||||||
def test_overwrite_store(self):
|
|
||||||
reg = Registry()
|
|
||||||
reg['foo'] = 123
|
|
||||||
|
|
||||||
with self.assertRaises(KeyError):
|
|
||||||
reg['foo'] = 456
|
|
||||||
|
|
||||||
def test_delete_store(self):
|
def test_delete_store(self):
|
||||||
reg = Registry()
|
reg = Registry({
|
||||||
reg['foo'] = 123
|
'foo': 123,
|
||||||
|
})
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
del reg['foo']
|
del reg['foo']
|
||||||
|
Loading…
Reference in New Issue
Block a user