diff --git a/netbox/core/management/commands/clearcache.py b/netbox/core/management/commands/clearcache.py index dd95013af..9c91efe77 100644 --- a/netbox/core/management/commands/clearcache.py +++ b/netbox/core/management/commands/clearcache.py @@ -1,7 +1,7 @@ from django.core.cache import cache from django.core.management.base import BaseCommand -from extras.models import ConfigRevision +from core.models import ConfigRevision class Command(BaseCommand): diff --git a/netbox/core/migrations/0009_configrevision.py b/netbox/core/migrations/0009_configrevision.py new file mode 100644 index 000000000..46b3764b5 --- /dev/null +++ b/netbox/core/migrations/0009_configrevision.py @@ -0,0 +1,31 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0008_contenttype_proxy'), + ] + + operations = [ + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.CreateModel( + name='ConfigRevision', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True)), + ('comment', models.CharField(blank=True, max_length=200)), + ('data', models.JSONField(blank=True, null=True)), + ], + options={ + 'verbose_name': 'config revision', + 'verbose_name_plural': 'config revisions', + 'ordering': ['-created'], + }, + ), + ], + # Table has been renamed from extras_configrevision + database_operations=[], + ), + ] diff --git a/netbox/core/models/__init__.py b/netbox/core/models/__init__.py index c93c392d7..2c30ce02b 100644 --- a/netbox/core/models/__init__.py +++ b/netbox/core/models/__init__.py @@ -1,3 +1,4 @@ +from .config import * from .contenttypes import * from .data import * from .files import * diff --git a/netbox/core/models/config.py b/netbox/core/models/config.py new file mode 100644 index 000000000..08326f5b9 --- /dev/null +++ b/netbox/core/models/config.py @@ -0,0 +1,66 @@ +from django.core.cache import cache +from django.db import models +from django.urls import reverse +from django.utils.translation import gettext, gettext_lazy as _ + +from utilities.querysets import RestrictedQuerySet + +__all__ = ( + 'ConfigRevision', +) + + +class ConfigRevision(models.Model): + """ + An atomic revision of NetBox's configuration. + """ + created = models.DateTimeField( + verbose_name=_('created'), + auto_now_add=True + ) + comment = models.CharField( + verbose_name=_('comment'), + max_length=200, + blank=True + ) + data = models.JSONField( + blank=True, + null=True, + verbose_name=_('configuration data') + ) + + objects = RestrictedQuerySet.as_manager() + + class Meta: + ordering = ['-created'] + verbose_name = _('config revision') + verbose_name_plural = _('config revisions') + + def __str__(self): + if not self.pk: + return gettext('Default configuration') + if self.is_active: + return gettext('Current configuration') + return gettext('Config revision #{id}').format(id=self.pk) + + def __getattr__(self, item): + if item in self.data: + return self.data[item] + return super().__getattribute__(item) + + def get_absolute_url(self): + if not self.pk: + return reverse('core:config') # Default config view + return reverse('extras:configrevision', args=[self.pk]) + + def activate(self): + """ + Cache the configuration data. + """ + cache.set('config', self.data, None) + cache.set('config_version', self.pk, None) + activate.alters_data = True + + @property + def is_active(self): + return cache.get('config_version') == self.pk diff --git a/netbox/core/views.py b/netbox/core/views.py index d16fa4ece..2152eb7f5 100644 --- a/netbox/core/views.py +++ b/netbox/core/views.py @@ -1,7 +1,7 @@ from django.contrib import messages from django.shortcuts import get_object_or_404, redirect -from extras.models import ConfigRevision +from core.models import ConfigRevision from netbox.config import get_config from netbox.views import generic from netbox.views.generic.base import BaseObjectView diff --git a/netbox/extras/filtersets.py b/netbox/extras/filtersets.py index 32850bee2..1443c0b9d 100644 --- a/netbox/extras/filtersets.py +++ b/netbox/extras/filtersets.py @@ -4,7 +4,7 @@ from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.utils.translation import gettext as _ -from core.models import DataSource +from core.models import ConfigRevision, DataSource from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet from tenancy.models import Tenant, TenantGroup diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py index 1a4d45f9a..01cd45bab 100644 --- a/netbox/extras/forms/model_forms.py +++ b/netbox/extras/forms/model_forms.py @@ -6,7 +6,7 @@ from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ from core.forms.mixins import SyncedDataMixin -from core.models import ContentType +from core.models import ConfigRevision, ContentType from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup from extras.choices import * from extras.models import * diff --git a/netbox/extras/migrations/0101_move_configrevision.py b/netbox/extras/migrations/0101_move_configrevision.py new file mode 100644 index 000000000..e07d6ed9d --- /dev/null +++ b/netbox/extras/migrations/0101_move_configrevision.py @@ -0,0 +1,24 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0100_customfield_ui_attrs'), + ] + + operations = [ + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.DeleteModel( + name='ConfigRevision', + ), + ], + database_operations=[ + migrations.AlterModelTable( + name='ConfigRevision', + table='core_configrevision', + ), + ], + ), + ] diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index 67b455ab4..d0a2e4b61 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -3,14 +3,13 @@ import urllib.parse from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey -from django.core.cache import cache from django.core.validators import ValidationError from django.db import models from django.http import HttpResponse from django.urls import reverse from django.utils import timezone from django.utils.formats import date_format -from django.utils.translation import gettext, gettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from rest_framework.utils.encoders import JSONEncoder from core.models import ContentType @@ -28,7 +27,6 @@ from utilities.utils import clean_html, dict_to_querydict, render_jinja2 __all__ = ( 'Bookmark', - 'ConfigRevision', 'CustomLink', 'ExportTemplate', 'ImageAttachment', @@ -710,59 +708,3 @@ class Bookmark(models.Model): raise ValidationError( _("Bookmarks cannot be assigned to this object type ({type}).").format(type=self.object_type) ) - - -class ConfigRevision(models.Model): - """ - An atomic revision of NetBox's configuration. - """ - created = models.DateTimeField( - verbose_name=_('created'), - auto_now_add=True - ) - comment = models.CharField( - verbose_name=_('comment'), - max_length=200, - blank=True - ) - data = models.JSONField( - blank=True, - null=True, - verbose_name=_('configuration data') - ) - - objects = RestrictedQuerySet.as_manager() - - class Meta: - ordering = ['-created'] - verbose_name = _('config revision') - verbose_name_plural = _('config revisions') - - def __str__(self): - if not self.pk: - return gettext('Default configuration') - if self.is_active: - return gettext('Current configuration') - return gettext('Config revision #{id}').format(id=self.pk) - - def __getattr__(self, item): - if item in self.data: - return self.data[item] - return super().__getattribute__(item) - - def get_absolute_url(self): - if not self.pk: - return reverse('core:config') # Default config view - return reverse('extras:configrevision', args=[self.pk]) - - def activate(self): - """ - Cache the configuration data. - """ - cache.set('config', self.data, None) - cache.set('config_version', self.pk, None) - activate.alters_data = True - - @property - def is_active(self): - return cache.get('config_version') == self.pk diff --git a/netbox/extras/signals.py b/netbox/extras/signals.py index 8bdaf523c..efecaddc8 100644 --- a/netbox/extras/signals.py +++ b/netbox/extras/signals.py @@ -8,13 +8,14 @@ from django.dispatch import receiver, Signal from django.utils.translation import gettext_lazy as _ from django_prometheus.models import model_deletes, model_inserts, model_updates +from core.models import ConfigRevision from extras.validators import CustomValidator from netbox.config import get_config from netbox.context import current_request, webhooks_queue from netbox.signals import post_clean from utilities.exceptions import AbortRequest from .choices import ObjectChangeActionChoices -from .models import ConfigRevision, CustomField, ObjectChange, TaggedItem +from .models import CustomField, ObjectChange, TaggedItem from .webhooks import enqueue_object, get_snapshots, serialize_for_webhook # diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index 54194c00f..02593e2ba 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -4,6 +4,7 @@ import django_tables2 as tables from django.conf import settings from django.utils.translation import gettext_lazy as _ +from core.models import ConfigRevision from extras.models import * from netbox.tables import NetBoxTable, columns from .template_code import * diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 0e8e3b0ea..ba2415f9e 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -11,7 +11,7 @@ from django.views.generic import View from core.choices import JobStatusChoices, ManagedFileRootPathChoices from core.forms import ManagedFileForm -from core.models import Job +from core.models import ConfigRevision, Job from core.tables import JobTable from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm from extras.dashboard.utils import get_widget_class diff --git a/netbox/netbox/config/__init__.py b/netbox/netbox/config/__init__.py index a9a93636c..c536ceadb 100644 --- a/netbox/netbox/config/__init__.py +++ b/netbox/netbox/config/__init__.py @@ -74,7 +74,7 @@ class Config: def _populate_from_db(self): """Cache data from latest ConfigRevision, then populate from cache""" - from extras.models import ConfigRevision + from core.models import ConfigRevision try: revision = ConfigRevision.objects.last() diff --git a/netbox/netbox/tests/test_config.py b/netbox/netbox/tests/test_config.py index db401cf0c..f8c892363 100644 --- a/netbox/netbox/tests/test_config.py +++ b/netbox/netbox/tests/test_config.py @@ -2,7 +2,7 @@ from django.conf import settings from django.core.cache import cache from django.test import override_settings, TestCase -from extras.models import ConfigRevision +from core.models import ConfigRevision from netbox.config import clear_config, get_config