Populate namespaced models & constants from all core apps and plugins

This commit is contained in:
Jeremy Stretch 2025-08-01 09:51:13 -04:00
parent 19f89acc3a
commit 1f04cff828

View File

@ -1,18 +1,18 @@
import code import code
import platform import platform
import sys from collections import defaultdict
from types import SimpleNamespace
from colorama import Fore, Style from colorama import Fore, Style
from django import get_version from django import get_version
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.utils.module_loading import import_string
from netbox.constants import CORE_APPS from netbox.constants import CORE_APPS
from netbox.plugins.utils import get_installed_plugins from netbox.plugins.utils import get_installed_plugins
EXCLUDE_MODELS = ()
def color(color: str, text: str): def color(color: str, text: str):
return getattr(Fore, color.upper()) + text + Style.RESET_ALL return getattr(Fore, color.upper()) + text + Style.RESET_ALL
@ -22,6 +22,29 @@ def bright(text: str):
return Style.BRIGHT + text + Style.RESET_ALL return Style.BRIGHT + text + Style.RESET_ALL
def get_models(app_config):
"""
Return a list of all non-private models within an app.
"""
return [
model for model in app_config.get_models()
if not getattr(model, '_netbox_private', False)
]
def get_constants(app_config):
"""
Return a dictionary mapping of all constants defined within an app.
"""
try:
constants = import_string(f'{app_config.name}.constants')
except ImportError:
return {}
return {
name: value for name, value in vars(constants).items()
}
class Command(BaseCommand): class Command(BaseCommand):
help = "Start the Django shell with all NetBox models already imported" help = "Start the Django shell with all NetBox models already imported"
django_models = {} django_models = {}
@ -40,37 +63,31 @@ class Command(BaseCommand):
print(f' {m}') print(f' {m}')
def get_namespace(self): def get_namespace(self):
namespace = {} namespace = defaultdict(SimpleNamespace)
# Gather Django models and constants from each app # Iterate through all core apps & plugins to compile namespace of models and constants
for app in CORE_APPS: for app_name in [*CORE_APPS, *get_installed_plugins().keys()]:
models = [] app_config = apps.get_app_config(app_name)
# Load models from each app # Populate models
for model in apps.get_app_config(app).get_models(): if models := get_models(app_config):
app_label = model._meta.app_label for model in models:
model_name = model._meta.model_name setattr(namespace[app_name], model.__name__, model)
if f'{app_label}.{model_name}' not in EXCLUDE_MODELS: self.django_models[app_name] = sorted([
namespace[model.__name__] = model model.__name__ for model in models
models.append(model.__name__) ])
self.django_models[app] = sorted(models)
# Constants # Populate constants
try: for const_name, const_value in get_constants(app_config).items():
app_constants = sys.modules[f'{app}.constants'] setattr(namespace[app_name], const_name, const_value)
for name in dir(app_constants):
namespace[name] = getattr(app_constants, name)
except KeyError:
pass
# Load convenience commands return {
namespace.update({ **namespace,
'lsmodels': self._lsmodels, 'lsmodels': self._lsmodels,
}) }
return namespace @staticmethod
def get_banner_text():
def get_banner_text(self):
lines = [ lines = [
'{title} ({hostname})'.format( '{title} ({hostname})'.format(
title=bright('NetBox interactive shell'), title=bright('NetBox interactive shell'),
@ -120,5 +137,4 @@ class Command(BaseCommand):
readline.parse_and_bind('tab: complete') readline.parse_and_bind('tab: complete')
# Run interactive shell # Run interactive shell
shell = code.interact(banner=self.get_banner_text(), local=namespace) return code.interact(banner=self.get_banner_text(), local=namespace)
return shell