From ea1801bad3f90ea7bfa257a3c0d6361d8d7f2ee2 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 24 Mar 2023 21:25:22 -0400 Subject: [PATCH] Refactor extras.models --- .../migrations/0091_create_managedfiles.py | 6 +- netbox/extras/models/__init__.py | 32 +--- netbox/extras/models/change_logging.py | 4 + netbox/extras/models/mixins.py | 27 ++++ netbox/extras/models/models.py | 144 +----------------- netbox/extras/models/reports.py | 67 ++++++++ netbox/extras/models/scripts.py | 67 ++++++++ netbox/extras/models/tags.py | 5 + 8 files changed, 179 insertions(+), 173 deletions(-) create mode 100644 netbox/extras/models/mixins.py create mode 100644 netbox/extras/models/reports.py create mode 100644 netbox/extras/models/scripts.py diff --git a/netbox/extras/migrations/0091_create_managedfiles.py b/netbox/extras/migrations/0091_create_managedfiles.py index 71581614a..6921247a4 100644 --- a/netbox/extras/migrations/0091_create_managedfiles.py +++ b/netbox/extras/migrations/0091_create_managedfiles.py @@ -3,7 +3,7 @@ import pkgutil from django.conf import settings from django.db import migrations, models -import extras.models.models +import extras.models.mixins def create_files(cls, root_name, root_path): @@ -60,7 +60,7 @@ class Migration(migrations.Migration): 'indexes': [], 'constraints': [], }, - bases=(extras.models.models.PythonModuleMixin, 'core.managedfile', models.Model), + bases=(extras.models.mixins.PythonModuleMixin, 'core.managedfile', models.Model), ), migrations.CreateModel( name='ScriptModule', @@ -71,7 +71,7 @@ class Migration(migrations.Migration): 'indexes': [], 'constraints': [], }, - bases=(extras.models.models.PythonModuleMixin, 'core.managedfile', models.Model), + bases=(extras.models.mixins.PythonModuleMixin, 'core.managedfile', models.Model), ), # Instantiate ManagedFiles to represent scripts & reports diff --git a/netbox/extras/models/__init__.py b/netbox/extras/models/__init__.py index b89b958f5..423219ccb 100644 --- a/netbox/extras/models/__init__.py +++ b/netbox/extras/models/__init__.py @@ -1,34 +1,10 @@ -from .change_logging import ObjectChange +from .change_logging import * from .configs import * from .customfields import CustomField from .dashboard import * from .models import * +from .reports import * +from .scripts import * from .search import * from .staging import * -from .tags import Tag, TaggedItem - -__all__ = ( - 'Branch', - 'CachedValue', - 'ConfigContext', - 'ConfigContextModel', - 'ConfigRevision', - 'ConfigTemplate', - 'CustomField', - 'CustomLink', - 'Dashboard', - 'ExportTemplate', - 'ImageAttachment', - 'JobResult', - 'JournalEntry', - 'ObjectChange', - 'Report', - 'ReportModule', - 'SavedFilter', - 'Script', - 'ScriptModule', - 'StagedChange', - 'Tag', - 'TaggedItem', - 'Webhook', -) +from .tags import * diff --git a/netbox/extras/models/change_logging.py b/netbox/extras/models/change_logging.py index 9660930c2..e2b118b84 100644 --- a/netbox/extras/models/change_logging.py +++ b/netbox/extras/models/change_logging.py @@ -7,6 +7,10 @@ from django.urls import reverse from extras.choices import * from utilities.querysets import RestrictedQuerySet +__all__ = ( + 'ObjectChange', +) + class ObjectChange(models.Model): """ diff --git a/netbox/extras/models/mixins.py b/netbox/extras/models/mixins.py new file mode 100644 index 000000000..82b0b9983 --- /dev/null +++ b/netbox/extras/models/mixins.py @@ -0,0 +1,27 @@ +import os +from pkgutil import ModuleInfo, get_importer + +__all__ = ( + 'PythonModuleMixin', +) + + +class PythonModuleMixin: + + @property + def path(self): + return os.path.splitext(self.file_path)[0] + + def get_module_info(self): + path = os.path.dirname(self.full_path) + module_name = os.path.basename(self.path) + return ModuleInfo( + module_finder=get_importer(path), + name=module_name, + ispkg=False + ) + + def get_module(self): + importer, module_name, _ = self.get_module_info() + module = importer.find_module(module_name).load_module(module_name) + return module diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index f1e70190b..1c6fe9828 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -1,9 +1,5 @@ -import inspect import json -import os import uuid -from functools import cached_property -from pkgutil import ModuleInfo, get_importer import django_rq from django.conf import settings @@ -22,18 +18,15 @@ from django.utils.formats import date_format from django.utils.translation import gettext as _ from rest_framework.utils.encoders import JSONEncoder -from core.choices import ManagedFileRootPathChoices -from core.models import ManagedFile from extras.choices import * from extras.conditions import ConditionSet from extras.constants import * -from extras.utils import FeatureQuery, image_upload, is_report, is_script +from extras.utils import FeatureQuery, image_upload from netbox.config import get_config from netbox.constants import RQ_QUEUE_DEFAULT from netbox.models import ChangeLoggedModel from netbox.models.features import ( - CloningMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, JobResultsMixin, SyncedDataMixin, - TagsMixin, WebhooksMixin, + CloningMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin, ) from utilities.querysets import RestrictedQuerySet from utilities.rqworker import get_queue_for_model @@ -46,11 +39,7 @@ __all__ = ( 'ImageAttachment', 'JobResult', 'JournalEntry', - 'Report', - 'ReportModule', 'SavedFilter', - 'Script', - 'ScriptModule', 'Webhook', ) @@ -816,132 +805,3 @@ class ConfigRevision(models.Model): @admin.display(boolean=True) def is_active(self): return cache.get('config_version') == self.pk - - -# -# Custom scripts & reports -# - -class PythonModuleMixin: - - @property - def path(self): - return os.path.splitext(self.file_path)[0] - - def get_module_info(self): - path = os.path.dirname(self.full_path) - module_name = os.path.basename(self.path) - return ModuleInfo( - module_finder=get_importer(path), - name=module_name, - ispkg=False - ) - - def get_module(self): - importer, module_name, _ = self.get_module_info() - module = importer.find_module(module_name).load_module(module_name) - return module - - -class Script(JobResultsMixin, WebhooksMixin, models.Model): - """ - Dummy model used to generate permissions for custom scripts. Does not exist in the database. - """ - class Meta: - managed = False - - -class ScriptModuleManager(models.Manager.from_queryset(RestrictedQuerySet)): - - def get_queryset(self): - return super().get_queryset().filter(file_root=ManagedFileRootPathChoices.SCRIPTS) - - -class ScriptModule(PythonModuleMixin, ManagedFile): - """ - Proxy model for script module files. - """ - objects = ScriptModuleManager() - - class Meta: - proxy = True - - def get_absolute_url(self): - return reverse('extras:script_list') - - @cached_property - def scripts(self): - - def _get_name(cls): - # For child objects in submodules use the full import path w/o the root module as the name - return cls.full_name.split(".", maxsplit=1)[1] - - module = self.get_module() - scripts = {} - ordered = getattr(module, 'script_order', []) - - for cls in ordered: - scripts[_get_name(cls)] = cls - for name, cls in inspect.getmembers(module, is_script): - if cls not in ordered: - scripts[_get_name(cls)] = cls - - return scripts - - def save(self, *args, **kwargs): - self.file_root = ManagedFileRootPathChoices.SCRIPTS - return super().save(*args, **kwargs) - - -# -# Reports -# - -class Report(JobResultsMixin, WebhooksMixin, models.Model): - """ - Dummy model used to generate permissions for reports. Does not exist in the database. - """ - class Meta: - managed = False - - -class ReportModuleManager(models.Manager.from_queryset(RestrictedQuerySet)): - - def get_queryset(self): - return super().get_queryset().filter(file_root=ManagedFileRootPathChoices.REPORTS) - - -class ReportModule(PythonModuleMixin, ManagedFile): - """ - Proxy model for report module files. - """ - objects = ReportModuleManager() - - class Meta: - proxy = True - - def get_absolute_url(self): - return reverse('extras:report_list') - - @cached_property - def reports(self): - - def _get_name(cls): - # For child objects in submodules use the full import path w/o the root module as the name - return cls.full_name.split(".", maxsplit=1)[1] - - module = self.get_module() - reports = {} - ordered = getattr(module, 'report_order', []) - - for cls in ordered: - reports[_get_name(cls)] = cls - for name, cls in inspect.getmembers(module, is_report): - if cls not in ordered: - reports[_get_name(cls)] = cls - - return reports - - def save(self, *args, **kwargs): - self.file_root = ManagedFileRootPathChoices.REPORTS - return super().save(*args, **kwargs) diff --git a/netbox/extras/models/reports.py b/netbox/extras/models/reports.py new file mode 100644 index 000000000..b88f43b9a --- /dev/null +++ b/netbox/extras/models/reports.py @@ -0,0 +1,67 @@ +import inspect +from functools import cached_property + +from django.db import models +from django.urls import reverse + +from core.choices import ManagedFileRootPathChoices +from core.models import ManagedFile +from extras.utils import is_report +from netbox.models.features import JobResultsMixin, WebhooksMixin +from utilities.querysets import RestrictedQuerySet +from .mixins import PythonModuleMixin + +__all__ = ( + 'Report', + 'ReportModule', +) + + +class Report(JobResultsMixin, WebhooksMixin, models.Model): + """ + Dummy model used to generate permissions for reports. Does not exist in the database. + """ + class Meta: + managed = False + + +class ReportModuleManager(models.Manager.from_queryset(RestrictedQuerySet)): + + def get_queryset(self): + return super().get_queryset().filter(file_root=ManagedFileRootPathChoices.REPORTS) + + +class ReportModule(PythonModuleMixin, ManagedFile): + """ + Proxy model for report module files. + """ + objects = ReportModuleManager() + + class Meta: + proxy = True + + def get_absolute_url(self): + return reverse('extras:report_list') + + @cached_property + def reports(self): + + def _get_name(cls): + # For child objects in submodules use the full import path w/o the root module as the name + return cls.full_name.split(".", maxsplit=1)[1] + + module = self.get_module() + reports = {} + ordered = getattr(module, 'report_order', []) + + for cls in ordered: + reports[_get_name(cls)] = cls + for name, cls in inspect.getmembers(module, is_report): + if cls not in ordered: + reports[_get_name(cls)] = cls + + return reports + + def save(self, *args, **kwargs): + self.file_root = ManagedFileRootPathChoices.REPORTS + return super().save(*args, **kwargs) diff --git a/netbox/extras/models/scripts.py b/netbox/extras/models/scripts.py new file mode 100644 index 000000000..6c369ecfa --- /dev/null +++ b/netbox/extras/models/scripts.py @@ -0,0 +1,67 @@ +import inspect +from functools import cached_property + +from django.db import models +from django.urls import reverse + +from core.choices import ManagedFileRootPathChoices +from core.models import ManagedFile +from extras.utils import is_script +from netbox.models.features import JobResultsMixin, WebhooksMixin +from utilities.querysets import RestrictedQuerySet +from .mixins import PythonModuleMixin + +__all__ = ( + 'Script', + 'ScriptModule', +) + + +class Script(JobResultsMixin, WebhooksMixin, models.Model): + """ + Dummy model used to generate permissions for custom scripts. Does not exist in the database. + """ + class Meta: + managed = False + + +class ScriptModuleManager(models.Manager.from_queryset(RestrictedQuerySet)): + + def get_queryset(self): + return super().get_queryset().filter(file_root=ManagedFileRootPathChoices.SCRIPTS) + + +class ScriptModule(PythonModuleMixin, ManagedFile): + """ + Proxy model for script module files. + """ + objects = ScriptModuleManager() + + class Meta: + proxy = True + + def get_absolute_url(self): + return reverse('extras:script_list') + + @cached_property + def scripts(self): + + def _get_name(cls): + # For child objects in submodules use the full import path w/o the root module as the name + return cls.full_name.split(".", maxsplit=1)[1] + + module = self.get_module() + scripts = {} + ordered = getattr(module, 'script_order', []) + + for cls in ordered: + scripts[_get_name(cls)] = cls + for name, cls in inspect.getmembers(module, is_script): + if cls not in ordered: + scripts[_get_name(cls)] = cls + + return scripts + + def save(self, *args, **kwargs): + self.file_root = ManagedFileRootPathChoices.SCRIPTS + return super().save(*args, **kwargs) diff --git a/netbox/extras/models/tags.py b/netbox/extras/models/tags.py index b980f0709..286ec7423 100644 --- a/netbox/extras/models/tags.py +++ b/netbox/extras/models/tags.py @@ -9,6 +9,11 @@ from netbox.models.features import ExportTemplatesMixin from utilities.choices import ColorChoices from utilities.fields import ColorField +__all__ = ( + 'Tag', + 'TaggedItem', +) + # # Tags