Skip to content

Commit 4e80f64

Browse files
authored
Merge pull request #2 from Kani999/reloading_tags
Add functionality to refresh tag form definitions
2 parents 940b24b + 26ed41c commit 4e80f64

4 files changed

Lines changed: 50 additions & 97 deletions

File tree

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ A NetBox plugin that dynamically reloads plugins without requiring a server rest
66

77
- Dynamically registers plugin models that were missed during server startup
88
- Refreshes custom field form definitions to include newly registered models
9+
- Refreshes tag form definitions to include newly registered models
910
- Helps solve integration issues between NetBox and other plugins
1011
- No configuration required - works out of the box
1112

@@ -14,6 +15,9 @@ A NetBox plugin that dynamically reloads plugins without requiring a server rest
1415
| NetBox Version | Plugin Version |
1516
|----------------|---------------|
1617
| 4.2.x | 0.0.2 |
18+
| 4.3.x | 4.3.x |
19+
20+
**Version Format**: X.X.Y where X.X = NetBox version (e.g., 4.3) and Y = plugin version increment
1721

1822
## Installation
1923

@@ -54,9 +58,10 @@ When NetBox starts, Plugin Reloader:
5458

5559
1. Scans all enabled plugins for models that aren't properly registered in NetBox's feature registry
5660
2. Registers any missed models with NetBox's registration system
57-
3. Refreshes form field definitions to ensure they include all registered models
61+
3. Refreshes custom field form definitions to ensure they include all registered models
62+
4. Refreshes tag form definitions to ensure they include all registered models
5863

59-
This helps resolve issues where plugins might not fully integrate with NetBox due to load order problems without requiring a server restart.
64+
This helps resolve issues where plugins might not fully integrate with NetBox due to load order problems without requiring a server restart. The reloader specifically updates custom field choices and tag choices to include newly registered plugin models.
6065

6166
## Contributing
6267

@@ -71,4 +76,4 @@ This project is licensed under the MIT License - see the LICENSE file for detail
7176
Jan Krupa <jan.krupa@cesnet.cz>
7277

7378
## Links
74-
- Based on https://github.com/netbox-community/netbox/discussions/17836
79+
- Based on https://github.com/netbox-community/netbox/discussions/17836

netbox_plugin_reloader/__init__.py

Lines changed: 41 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from netbox.plugins import PluginConfig
6+
67
from netbox_plugin_reloader.version import __version__
78

89

@@ -19,149 +20,97 @@ class NetboxPluginReloaderConfig(PluginConfig):
1920
description = "Dynamically reload NetBox plugins without server restart"
2021
version = __version__
2122
base_url = "netbox-plugin-reloader"
22-
23-
# Plugin configuration
24-
default_settings = {}
25-
required_settings = []
26-
27-
# NetBox version compatibility
2823
min_version = "4.3.0"
2924
max_version = "4.3.99"
3025

3126
def ready(self):
3227
"""
33-
Plugin initialization logic executed when Django loads the application.
34-
35-
This method handles the dynamic registration of plugin models and
36-
refreshes form fields to ensure all plugins are properly loaded.
28+
Initializes the plugin when the Django application loads.
29+
30+
Registers any plugin models missed during startup and refreshes form fields to include newly registered models for custom fields and tags.
3731
"""
38-
# Initialize parent plugin functionality
3932
super().ready()
4033

41-
# Import dependencies
4234
from core.models import ObjectType
4335
from django.apps import apps
4436
from django.conf import settings
4537
from django.utils.translation import gettext_lazy as _
46-
from extras.forms.model_forms import CustomFieldForm
38+
from extras.forms.model_forms import CustomFieldForm, TagForm
4739
from netbox.models.features import FEATURES_MAP, register_models
4840
from netbox.registry import registry
4941
from utilities.forms.fields import ContentTypeMultipleChoiceField
5042

51-
# Step 1: Register any plugin models missed during initial application startup
52-
self._register_missing_plugin_models(
53-
plugin_list=settings.PLUGINS,
54-
app_registry=apps,
55-
netbox_registry=registry,
56-
feature_mixins_map=FEATURES_MAP,
57-
model_register_function=register_models,
58-
)
43+
# Register missing plugin models
44+
self._register_missing_plugin_models(settings.PLUGINS, apps, registry, FEATURES_MAP, register_models)
5945

60-
# Step 2: Ensure form fields for plugins are properly initialized
61-
self._refresh_custom_field_form(
62-
form_class=CustomFieldForm,
63-
field_class=ContentTypeMultipleChoiceField,
64-
object_type_class=ObjectType,
65-
translation_function=_,
66-
)
46+
# Refresh form fields
47+
self._refresh_form_field(CustomFieldForm, "custom_fields", ObjectType, ContentTypeMultipleChoiceField, _)
48+
self._refresh_form_field(TagForm, "tags", ObjectType, ContentTypeMultipleChoiceField, _)
6749

6850
def _register_missing_plugin_models(
69-
self,
70-
plugin_list,
71-
app_registry,
72-
netbox_registry,
73-
feature_mixins_map,
74-
model_register_function,
51+
self, plugin_list, app_registry, netbox_registry, feature_mixins_map, model_register_function
7552
):
7653
"""
77-
Register plugin models that weren't properly registered during application startup.
78-
79-
This method scans all enabled plugins, identifies models that haven't been
80-
registered in NetBox's feature registry, and registers them.
81-
82-
Args:
83-
plugin_list: List of enabled plugin names from settings
84-
app_registry: Django application registry
85-
netbox_registry: NetBox's internal registry for tracking features
86-
feature_mixins_map: Dictionary mapping feature names to mixin classes
87-
model_register_function: Function used to register models with NetBox
54+
Registers plugin models that were not registered during initial application startup.
55+
56+
Iterates through the provided list of plugin names, identifies models that are missing from the NetBox feature registry, and registers them using the supplied registration function. Prints errors encountered during processing and reports the number of models registered if any were missed.
8857
"""
8958
unregistered_models = []
9059

91-
# For each enabled plugin
9260
for plugin_name in plugin_list:
9361
try:
94-
# Get the Django app configuration for this plugin
9562
plugin_app_config = app_registry.get_app_config(plugin_name)
9663
app_label = plugin_app_config.label
9764

98-
# Check each model in the plugin
9965
for model_class in plugin_app_config.get_models():
10066
model_name = model_class._meta.model_name
101-
102-
# Only register models that aren't already in the registry
103-
if not self._is_model_registered(
104-
app_label=app_label,
105-
model_name=model_name,
106-
registry=netbox_registry,
107-
feature_mixins_map=feature_mixins_map,
108-
):
67+
if not self._is_model_registered(app_label, model_name, netbox_registry, feature_mixins_map):
10968
unregistered_models.append(model_class)
11069

11170
except Exception as e:
112-
# Safely handle errors with specific plugins
11371
print(f"Error processing plugin {plugin_name}: {e}")
11472

115-
# Register the collected models if any were found
11673
if unregistered_models:
11774
model_register_function(*unregistered_models)
11875
print(f"Plugin Reloader: Registered {len(unregistered_models)} previously missed models")
11976

12077
def _is_model_registered(self, app_label, model_name, registry, feature_mixins_map):
12178
"""
122-
Check if a model is already registered in any NetBox feature registry.
123-
124-
Args:
125-
app_label: Django application label (e.g., 'dcim', 'ipam')
126-
model_name: Model name without the app label
127-
registry: NetBox registry containing feature registrations
128-
feature_mixins_map: Dictionary mapping feature names to mixin classes
129-
79+
Determines whether a model is registered under any NetBox feature.
80+
13081
Returns:
131-
bool: True if model is registered in any feature, False otherwise
82+
True if the specified model is present in any feature registry; otherwise, False.
13283
"""
133-
# Check each available feature registry
134-
for feature_name in feature_mixins_map.keys():
135-
feature_registry = registry["model_features"][feature_name]
136-
137-
# If the app_label exists and the model is registered under it
138-
if app_label in feature_registry and model_name in feature_registry[app_label]:
139-
return True
140-
141-
# Model not found in any feature registry
142-
return False
84+
return any(
85+
app_label in registry["model_features"][feature_name]
86+
and model_name in registry["model_features"][feature_name][app_label]
87+
for feature_name in feature_mixins_map.keys()
88+
)
14389

144-
def _refresh_custom_field_form(self, form_class, field_class, object_type_class, translation_function):
90+
def _refresh_form_field(self, form_class, feature_name, object_type_class, field_class, translation_function):
14591
"""
146-
Refresh form field definitions for custom fields.
147-
148-
This ensures that plugin models are properly included in form field choices
149-
after they've been registered.
150-
92+
Updates a form class's object_types field to reflect models supporting a specific NetBox feature.
93+
15194
Args:
152-
form_class: The CustomFieldForm class to update
153-
field_class: Field class to use for the object_types field
154-
object_type_class: The ObjectType model class
155-
translation_function: Function for internationalizing strings
95+
form_class: The form class to update.
96+
feature_name: The NetBox feature name (e.g., "custom_fields", "tags").
97+
object_type_class: The ContentType-like class used to query object types.
98+
field_class: The form field class to instantiate.
99+
translation_function: Function used to translate field labels and help texts.
156100
"""
157-
# Create a field that includes all models with custom_fields feature
101+
field_labels = {
102+
"custom_fields": ("Object types", "The type(s) of object that have this custom field"),
103+
"tags": ("Object types", "The type(s) of object that can have this tag"),
104+
}
105+
106+
label, help_text = field_labels[feature_name]
107+
158108
object_types_field = field_class(
159-
label=translation_function("Object types"),
160-
queryset=object_type_class.objects.with_feature("custom_fields"),
161-
help_text=translation_function("The type(s) of object that have this custom field"),
109+
label=translation_function(label),
110+
queryset=object_type_class.objects.with_feature(feature_name),
111+
help_text=translation_function(help_text),
162112
)
163113

164-
# Update the form definition
165114
form_class.base_fields["object_types"] = object_types_field
166115

167116

netbox_plugin_reloader/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Version information."""
22

3-
__version__ = "4.3.0"
3+
__version__ = "4.3.1"

setup.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ def get_version(rel_path):
3838
'Framework :: Django :: 5.0',
3939
'License :: OSI Approved :: Apache Software License',
4040
'Programming Language :: Python :: 3',
41-
'Programming Language :: Python :: 3.9',
4241
'Programming Language :: Python :: 3.10',
4342
'Operating System :: OS Independent',
4443
],

0 commit comments

Comments
 (0)