From 19b78e63ce29f9a5df97bb7b68c9225eb6ad01ed Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 10 Mar 2021 13:35:13 -0500 Subject: [PATCH 1/6] Introduce ChangeLoggedModel as a standard model --- netbox/circuits/models.py | 4 ++-- netbox/dcim/models/device_component_templates.py | 4 ++-- netbox/extras/models/models.py | 4 ++-- netbox/extras/models/tags.py | 4 ++-- netbox/netbox/models.py | 11 +++++++++-- netbox/virtualization/models.py | 4 ++-- 6 files changed, 19 insertions(+), 12 deletions(-) diff --git a/netbox/circuits/models.py b/netbox/circuits/models.py index 654207735..ff8baff01 100644 --- a/netbox/circuits/models.py +++ b/netbox/circuits/models.py @@ -6,7 +6,7 @@ from dcim.fields import ASNField from dcim.models import CableTermination, PathEndpoint from extras.models import ObjectChange, TaggedItem from extras.utils import extras_features -from netbox.models import BigIDModel, ChangeLoggingMixin, OrganizationalModel, PrimaryModel +from netbox.models import BigIDModel, ChangeLoggedModel, OrganizationalModel, PrimaryModel from utilities.querysets import RestrictedQuerySet from .choices import * from .querysets import CircuitQuerySet @@ -234,7 +234,7 @@ class Circuit(PrimaryModel): return self._get_termination('Z') -class CircuitTermination(ChangeLoggingMixin, BigIDModel, PathEndpoint, CableTermination): +class CircuitTermination(ChangeLoggedModel, PathEndpoint, CableTermination): circuit = models.ForeignKey( to='circuits.Circuit', on_delete=models.CASCADE, diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index 3e81cd3f2..b02f1bb79 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -5,7 +5,7 @@ from django.db import models from dcim.choices import * from dcim.constants import * from extras.utils import extras_features -from netbox.models import BigIDModel, ChangeLoggingMixin +from netbox.models import ChangeLoggedModel from utilities.fields import NaturalOrderingField from utilities.querysets import RestrictedQuerySet from utilities.ordering import naturalize_interface @@ -26,7 +26,7 @@ __all__ = ( ) -class ComponentTemplateModel(ChangeLoggingMixin, BigIDModel): +class ComponentTemplateModel(ChangeLoggedModel): device_type = models.ForeignKey( to='dcim.DeviceType', on_delete=models.CASCADE, diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index 5cf700233..1fd183b8a 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -16,7 +16,7 @@ from extras.choices import * from extras.constants import * from extras.querysets import ConfigContextQuerySet from extras.utils import extras_features, FeatureQuery, image_upload -from netbox.models import BigIDModel, ChangeLoggingMixin +from netbox.models import BigIDModel, ChangeLoggedModel from utilities.querysets import RestrictedQuerySet from utilities.utils import deepmerge, render_jinja2 @@ -379,7 +379,7 @@ class ImageAttachment(BigIDModel): # Config contexts # -class ConfigContext(ChangeLoggingMixin, BigIDModel): +class ConfigContext(ChangeLoggedModel): """ A ConfigContext represents a set of arbitrary data available to any Device or VirtualMachine matching its assigned qualifiers (region, site, etc.). For example, the data stored in a ConfigContext assigned to site A and tenant B diff --git a/netbox/extras/models/tags.py b/netbox/extras/models/tags.py index 3a5b539b1..d7266f359 100644 --- a/netbox/extras/models/tags.py +++ b/netbox/extras/models/tags.py @@ -2,7 +2,7 @@ from django.db import models from django.utils.text import slugify from taggit.models import TagBase, GenericTaggedItemBase -from netbox.models import BigIDModel, ChangeLoggingMixin +from netbox.models import BigIDModel, ChangeLoggedModel from utilities.choices import ColorChoices from utilities.fields import ColorField from utilities.querysets import RestrictedQuerySet @@ -12,7 +12,7 @@ from utilities.querysets import RestrictedQuerySet # Tags # -class Tag(ChangeLoggingMixin, BigIDModel, TagBase): +class Tag(ChangeLoggedModel, TagBase): color = ColorField( default=ColorChoices.COLOR_GREY ) diff --git a/netbox/netbox/models.py b/netbox/netbox/models.py index acaa90656..c36586f36 100644 --- a/netbox/netbox/models.py +++ b/netbox/netbox/models.py @@ -12,8 +12,7 @@ from utilities.utils import serialize_object __all__ = ( 'BigIDModel', - 'ChangeLoggingMixin', - 'CustomFieldsMixin', + 'ChangeLoggedModel', 'NestedGroupModel', 'OrganizationalModel', 'PrimaryModel', @@ -137,6 +136,14 @@ class BigIDModel(models.Model): abstract = True +class ChangeLoggedModel(ChangeLoggingMixin, BigIDModel): + """ + Base model for all objects which support change logging. + """ + class Meta: + abstract = True + + class PrimaryModel(ChangeLoggingMixin, CustomFieldsMixin, BigIDModel): """ Primary models represent real objects within the infrastructure being modeled. diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index 4280c6b8e..9744df3a5 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -9,7 +9,7 @@ from dcim.models import BaseInterface, Device from extras.models import ConfigContextModel, TaggedItem from extras.querysets import ConfigContextModelQuerySet from extras.utils import extras_features -from netbox.models import BigIDModel, ChangeLoggingMixin, OrganizationalModel, PrimaryModel +from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel from utilities.fields import NaturalOrderingField from utilities.ordering import naturalize_interface from utilities.query_functions import CollateAsChar @@ -374,7 +374,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): # TODO: Inherit from PrimaryModel @extras_features('export_templates', 'webhooks') -class VMInterface(ChangeLoggingMixin, BigIDModel, BaseInterface): +class VMInterface(ChangeLoggedModel, BaseInterface): virtual_machine = models.ForeignKey( to='virtualization.VirtualMachine', on_delete=models.CASCADE, From ac0c54cce402148d5e568e5b5b5f7793c2158d56 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 10 Mar 2021 13:49:10 -0500 Subject: [PATCH 2/6] #5401: Add custom field support for VMInterface --- netbox/templates/virtualization/vminterface.html | 3 ++- netbox/templates/virtualization/vminterface_edit.html | 8 ++++++++ netbox/virtualization/api/serializers.py | 4 ++-- netbox/virtualization/filters.py | 2 +- netbox/virtualization/forms.py | 2 +- .../virtualization/migrations/0020_standardize_models.py | 5 +++++ netbox/virtualization/models.py | 7 +++---- 7 files changed, 22 insertions(+), 9 deletions(-) diff --git a/netbox/templates/virtualization/vminterface.html b/netbox/templates/virtualization/vminterface.html index e3779ef21..e574e926e 100644 --- a/netbox/templates/virtualization/vminterface.html +++ b/netbox/templates/virtualization/vminterface.html @@ -57,9 +57,10 @@ - {% plugin_left_page object %} + {% plugin_left_page object %}
+ {% include 'inc/custom_fields_panel.html' %} {% include 'extras/inc/tags_panel.html' with tags=object.tags.all %} {% plugin_right_page object %}
diff --git a/netbox/templates/virtualization/vminterface_edit.html b/netbox/templates/virtualization/vminterface_edit.html index d4869aaf9..c0ad6e98c 100644 --- a/netbox/templates/virtualization/vminterface_edit.html +++ b/netbox/templates/virtualization/vminterface_edit.html @@ -31,6 +31,14 @@ {% render_field form.tagged_vlans %} + {% if form.custom_fields %} +
+
Custom Fields
+
+ {% render_custom_fields form %} +
+
+ {% endif %} {% endblock %} {% block buttons %} diff --git a/netbox/virtualization/api/serializers.py b/netbox/virtualization/api/serializers.py index e3b82e40a..f2640811b 100644 --- a/netbox/virtualization/api/serializers.py +++ b/netbox/virtualization/api/serializers.py @@ -103,7 +103,7 @@ class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer): # VM interfaces # -class VMInterfaceSerializer(TaggedObjectSerializer, ValidatedModelSerializer): +class VMInterfaceSerializer(TaggedObjectSerializer, CustomFieldModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:vminterface-detail') virtual_machine = NestedVirtualMachineSerializer() mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False) @@ -119,7 +119,7 @@ class VMInterfaceSerializer(TaggedObjectSerializer, ValidatedModelSerializer): model = VMInterface fields = [ 'id', 'url', 'virtual_machine', 'name', 'enabled', 'mtu', 'mac_address', 'description', 'mode', - 'untagged_vlan', 'tagged_vlans', 'tags', + 'untagged_vlan', 'tagged_vlans', 'tags', 'custom_fields', 'created', 'last_updated', ] def validate(self, data): diff --git a/netbox/virtualization/filters.py b/netbox/virtualization/filters.py index ea668c737..be9b70749 100644 --- a/netbox/virtualization/filters.py +++ b/netbox/virtualization/filters.py @@ -237,7 +237,7 @@ class VirtualMachineFilterSet( return queryset.exclude(params) -class VMInterfaceFilterSet(BaseFilterSet): +class VMInterfaceFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet): q = django_filters.CharFilter( method='search', label='Search', diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py index c2d0aee38..2aed5230f 100644 --- a/netbox/virtualization/forms.py +++ b/netbox/virtualization/forms.py @@ -576,7 +576,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil # VM interfaces # -class VMInterfaceForm(BootstrapMixin, InterfaceCommonForm, forms.ModelForm): +class VMInterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm): untagged_vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, diff --git a/netbox/virtualization/migrations/0020_standardize_models.py b/netbox/virtualization/migrations/0020_standardize_models.py index 42c44e83a..42dedbc72 100644 --- a/netbox/virtualization/migrations/0020_standardize_models.py +++ b/netbox/virtualization/migrations/0020_standardize_models.py @@ -54,4 +54,9 @@ class Migration(migrations.Migration): name='last_updated', field=models.DateTimeField(auto_now=True, null=True), ), + migrations.AddField( + model_name='vminterface', + name='custom_field_data', + field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), + ), ] diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index 9744df3a5..4896e26bd 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -9,7 +9,7 @@ from dcim.models import BaseInterface, Device from extras.models import ConfigContextModel, TaggedItem from extras.querysets import ConfigContextModelQuerySet from extras.utils import extras_features -from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel +from netbox.models import OrganizationalModel, PrimaryModel from utilities.fields import NaturalOrderingField from utilities.ordering import naturalize_interface from utilities.query_functions import CollateAsChar @@ -372,9 +372,8 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): # Interfaces # -# TODO: Inherit from PrimaryModel -@extras_features('export_templates', 'webhooks') -class VMInterface(ChangeLoggedModel, BaseInterface): +@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +class VMInterface(PrimaryModel, BaseInterface): virtual_machine = models.ForeignKey( to='virtualization.VirtualMachine', on_delete=models.CASCADE, From 20f60cd99d46d76da9f8c79b946affb7fcd249c3 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 10 Mar 2021 14:03:50 -0500 Subject: [PATCH 3/6] Fix feature assignments for device component templates --- netbox/dcim/models/device_component_templates.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index b02f1bb79..d99d6b7c3 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -76,7 +76,7 @@ class ComponentTemplateModel(ChangeLoggedModel): return super().to_objectchange(action, related_object=device_type) -@extras_features('custom_fields', 'export_templates', 'webhooks') +@extras_features('webhooks') class ConsolePortTemplate(ComponentTemplateModel): """ A template for a ConsolePort to be created for a new Device. @@ -100,7 +100,7 @@ class ConsolePortTemplate(ComponentTemplateModel): ) -@extras_features('custom_fields', 'export_templates', 'webhooks') +@extras_features('webhooks') class ConsoleServerPortTemplate(ComponentTemplateModel): """ A template for a ConsoleServerPort to be created for a new Device. @@ -124,7 +124,7 @@ class ConsoleServerPortTemplate(ComponentTemplateModel): ) -@extras_features('custom_fields', 'export_templates', 'webhooks') +@extras_features('webhooks') class PowerPortTemplate(ComponentTemplateModel): """ A template for a PowerPort to be created for a new Device. @@ -171,7 +171,7 @@ class PowerPortTemplate(ComponentTemplateModel): }) -@extras_features('custom_fields', 'export_templates', 'webhooks') +@extras_features('webhooks') class PowerOutletTemplate(ComponentTemplateModel): """ A template for a PowerOutlet to be created for a new Device. @@ -223,7 +223,7 @@ class PowerOutletTemplate(ComponentTemplateModel): ) -@extras_features('custom_fields', 'export_templates', 'webhooks') +@extras_features('webhooks') class InterfaceTemplate(ComponentTemplateModel): """ A template for a physical data interface on a new Device. @@ -258,7 +258,7 @@ class InterfaceTemplate(ComponentTemplateModel): ) -@extras_features('custom_fields', 'export_templates', 'webhooks') +@extras_features('webhooks') class FrontPortTemplate(ComponentTemplateModel): """ Template for a pass-through port on the front of a new Device. @@ -319,7 +319,7 @@ class FrontPortTemplate(ComponentTemplateModel): ) -@extras_features('custom_fields', 'export_templates', 'webhooks') +@extras_features('webhooks') class RearPortTemplate(ComponentTemplateModel): """ Template for a pass-through port on the rear of a new Device. @@ -350,7 +350,7 @@ class RearPortTemplate(ComponentTemplateModel): ) -@extras_features('custom_fields', 'export_templates', 'webhooks') +@extras_features('webhooks') class DeviceBayTemplate(ComponentTemplateModel): """ A template for a DeviceBay to be created for a new parent Device. From fc5bb966f0608fa2393a3ecbcfe557cdc7c3c9dd Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 10 Mar 2021 14:32:50 -0500 Subject: [PATCH 4/6] Declare TaggableManager on PrimaryModel base class --- netbox/circuits/models.py | 5 +---- netbox/dcim/models/cables.py | 3 --- netbox/dcim/models/device_components.py | 12 ------------ netbox/dcim/models/devices.py | 6 +----- netbox/dcim/models/power.py | 4 ---- netbox/dcim/models/racks.py | 4 ---- netbox/dcim/models/sites.py | 3 --- netbox/ipam/models/ip.py | 5 ----- netbox/ipam/models/services.py | 3 --- netbox/ipam/models/vlans.py | 3 --- netbox/ipam/models/vrfs.py | 4 ---- netbox/netbox/models.py | 4 ++-- netbox/secrets/models.py | 3 --- netbox/tenancy/models.py | 3 --- .../migrations/0020_standardize_models.py | 6 ++++++ netbox/virtualization/models.py | 9 +-------- 16 files changed, 11 insertions(+), 66 deletions(-) diff --git a/netbox/circuits/models.py b/netbox/circuits/models.py index ff8baff01..1ca98efd6 100644 --- a/netbox/circuits/models.py +++ b/netbox/circuits/models.py @@ -1,10 +1,9 @@ from django.db import models from django.urls import reverse -from taggit.managers import TaggableManager from dcim.fields import ASNField from dcim.models import CableTermination, PathEndpoint -from extras.models import ObjectChange, TaggedItem +from extras.models import ObjectChange from extras.utils import extras_features from netbox.models import BigIDModel, ChangeLoggedModel, OrganizationalModel, PrimaryModel from utilities.querysets import RestrictedQuerySet @@ -60,7 +59,6 @@ class Provider(PrimaryModel): comments = models.TextField( blank=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() @@ -184,7 +182,6 @@ class Circuit(PrimaryModel): ) objects = CircuitQuerySet.as_manager() - tags = TaggableManager(through=TaggedItem) csv_headers = [ 'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments', diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 06ae150d4..c8166cb44 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -6,13 +6,11 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db import models from django.db.models import Sum from django.urls import reverse -from taggit.managers import TaggableManager from dcim.choices import * from dcim.constants import * from dcim.fields import PathField from dcim.utils import decompile_path_node, object_to_path_node, path_node_to_object -from extras.models import TaggedItem from extras.utils import extras_features from netbox.models import BigIDModel, PrimaryModel from utilities.fields import ColorField @@ -108,7 +106,6 @@ class Cable(PrimaryModel): blank=True, null=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index e76d03826..4b162b30f 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -6,12 +6,10 @@ from django.db import models from django.db.models import Sum from django.urls import reverse from mptt.models import MPTTModel, TreeForeignKey -from taggit.managers import TaggableManager from dcim.choices import * from dcim.constants import * from dcim.fields import MACAddressField -from extras.models import TaggedItem from extras.utils import extras_features from netbox.models import PrimaryModel from utilities.fields import NaturalOrderingField @@ -227,7 +225,6 @@ class ConsolePort(ComponentModel, CableTermination, PathEndpoint): null=True, help_text='Port speed in bits per second' ) - tags = TaggableManager(through=TaggedItem) csv_headers = ['device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description'] @@ -271,7 +268,6 @@ class ConsoleServerPort(ComponentModel, CableTermination, PathEndpoint): null=True, help_text='Port speed in bits per second' ) - tags = TaggableManager(through=TaggedItem) csv_headers = ['device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description'] @@ -321,7 +317,6 @@ class PowerPort(ComponentModel, CableTermination, PathEndpoint): validators=[MinValueValidator(1)], help_text="Allocated power draw (watts)" ) - tags = TaggableManager(through=TaggedItem) csv_headers = [ 'device', 'name', 'label', 'type', 'mark_connected', 'maximum_draw', 'allocated_draw', 'description', @@ -434,7 +429,6 @@ class PowerOutlet(ComponentModel, CableTermination, PathEndpoint): blank=True, help_text="Phase (for three-phase feeds)" ) - tags = TaggableManager(through=TaggedItem) csv_headers = ['device', 'name', 'label', 'type', 'mark_connected', 'power_port', 'feed_leg', 'description'] @@ -568,7 +562,6 @@ class Interface(ComponentModel, BaseInterface, CableTermination, PathEndpoint): object_id_field='assigned_object_id', related_query_name='interface' ) - tags = TaggableManager(through=TaggedItem) csv_headers = [ 'device', 'name', 'label', 'parent', 'lag', 'type', 'enabled', 'mark_connected', 'mac_address', 'mtu', @@ -705,7 +698,6 @@ class FrontPort(ComponentModel, CableTermination): MaxValueValidator(REARPORT_POSITIONS_MAX) ] ) - tags = TaggableManager(through=TaggedItem) csv_headers = [ 'device', 'name', 'label', 'type', 'mark_connected', 'rear_port', 'rear_port_position', 'description', @@ -766,7 +758,6 @@ class RearPort(ComponentModel, CableTermination): MaxValueValidator(REARPORT_POSITIONS_MAX) ] ) - tags = TaggableManager(through=TaggedItem) csv_headers = ['device', 'name', 'label', 'type', 'mark_connected', 'positions', 'description'] @@ -816,7 +807,6 @@ class DeviceBay(ComponentModel): blank=True, null=True ) - tags = TaggableManager(through=TaggedItem) csv_headers = ['device', 'name', 'label', 'installed_device', 'description'] @@ -909,8 +899,6 @@ class InventoryItem(MPTTModel, ComponentModel): help_text='This item was automatically discovered' ) - tags = TaggableManager(through=TaggedItem) - objects = TreeManager() csv_headers = [ diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index 54a34aa57..7f22d9325 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -9,11 +9,10 @@ from django.db import models from django.db.models import F, ProtectedError from django.urls import reverse from django.utils.safestring import mark_safe -from taggit.managers import TaggableManager from dcim.choices import * from dcim.constants import * -from extras.models import ConfigContextModel, TaggedItem +from extras.models import ConfigContextModel from extras.querysets import ConfigContextModelQuerySet from extras.utils import extras_features from netbox.models import OrganizationalModel, PrimaryModel @@ -136,7 +135,6 @@ class DeviceType(PrimaryModel): comments = models.TextField( blank=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() @@ -601,7 +599,6 @@ class Device(PrimaryModel, ConfigContextModel): object_id_field='assigned_object_id', related_query_name='device' ) - tags = TaggableManager(through=TaggedItem) objects = ConfigContextModelQuerySet.as_manager() @@ -916,7 +913,6 @@ class VirtualChassis(PrimaryModel): max_length=30, blank=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/dcim/models/power.py b/netbox/dcim/models/power.py index db98f2fba..06d234149 100644 --- a/netbox/dcim/models/power.py +++ b/netbox/dcim/models/power.py @@ -2,11 +2,9 @@ from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from django.urls import reverse -from taggit.managers import TaggableManager from dcim.choices import * from dcim.constants import * -from extras.models import TaggedItem from extras.utils import extras_features from netbox.models import PrimaryModel from utilities.querysets import RestrictedQuerySet @@ -41,7 +39,6 @@ class PowerPanel(PrimaryModel): name = models.CharField( max_length=100 ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() @@ -133,7 +130,6 @@ class PowerFeed(PrimaryModel, PathEndpoint, CableTermination): comments = models.TextField( blank=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index 1185f6f87..6031b47ac 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -11,12 +11,10 @@ from django.db import models from django.db.models import Count, Sum from django.urls import reverse from mptt.models import TreeForeignKey -from taggit.managers import TaggableManager from dcim.choices import * from dcim.constants import * from dcim.elevations import RackElevationSVG -from extras.models import TaggedItem from extras.utils import extras_features from netbox.models import NestedGroupModel, OrganizationalModel, PrimaryModel from utilities.choices import ColorChoices @@ -251,7 +249,6 @@ class Rack(PrimaryModel): images = GenericRelation( to='extras.ImageAttachment' ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() @@ -558,7 +555,6 @@ class RackReservation(PrimaryModel): description = models.CharField( max_length=200 ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/dcim/models/sites.py b/netbox/dcim/models/sites.py index 1d26cd44a..eaa4ee861 100644 --- a/netbox/dcim/models/sites.py +++ b/netbox/dcim/models/sites.py @@ -2,13 +2,11 @@ from django.contrib.contenttypes.fields import GenericRelation from django.db import models from django.urls import reverse from mptt.models import TreeForeignKey -from taggit.managers import TaggableManager from timezone_field import TimeZoneField from dcim.choices import * from dcim.constants import * from dcim.fields import ASNField -from extras.models import TaggedItem from extras.utils import extras_features from netbox.models import NestedGroupModel, PrimaryModel from utilities.fields import NaturalOrderingField @@ -232,7 +230,6 @@ class Site(PrimaryModel): images = GenericRelation( to='extras.ImageAttachment' ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index c2c4fbf82..9867f6069 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -6,10 +6,8 @@ from django.core.exceptions import ValidationError from django.db import models from django.db.models import F from django.urls import reverse -from taggit.managers import TaggableManager from dcim.models import Device -from extras.models import TaggedItem from extras.utils import extras_features from netbox.models import OrganizationalModel, PrimaryModel from ipam.choices import * @@ -107,7 +105,6 @@ class Aggregate(PrimaryModel): max_length=200, blank=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() @@ -292,7 +289,6 @@ class Prefix(PrimaryModel): max_length=200, blank=True ) - tags = TaggableManager(through=TaggedItem) objects = PrefixQuerySet.as_manager() @@ -564,7 +560,6 @@ class IPAddress(PrimaryModel): max_length=200, blank=True ) - tags = TaggableManager(through=TaggedItem) objects = IPAddressManager() diff --git a/netbox/ipam/models/services.py b/netbox/ipam/models/services.py index 57338cce0..336f3a269 100644 --- a/netbox/ipam/models/services.py +++ b/netbox/ipam/models/services.py @@ -3,9 +3,7 @@ from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from django.urls import reverse -from taggit.managers import TaggableManager -from extras.models import TaggedItem from extras.utils import extras_features from ipam.choices import * from ipam.constants import * @@ -66,7 +64,6 @@ class Service(PrimaryModel): max_length=200, blank=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py index 131212564..298d11952 100644 --- a/netbox/ipam/models/vlans.py +++ b/netbox/ipam/models/vlans.py @@ -2,10 +2,8 @@ from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from django.urls import reverse -from taggit.managers import TaggableManager from dcim.models import Interface -from extras.models import TaggedItem from extras.utils import extras_features from ipam.choices import * from ipam.constants import * @@ -135,7 +133,6 @@ class VLAN(PrimaryModel): max_length=200, blank=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/ipam/models/vrfs.py b/netbox/ipam/models/vrfs.py index bcd5a456a..9eb2c6ab6 100644 --- a/netbox/ipam/models/vrfs.py +++ b/netbox/ipam/models/vrfs.py @@ -1,8 +1,6 @@ from django.db import models from django.urls import reverse -from taggit.managers import TaggableManager -from extras.models import TaggedItem from extras.utils import extras_features from ipam.constants import * from netbox.models import PrimaryModel @@ -59,7 +57,6 @@ class VRF(PrimaryModel): related_name='exporting_vrfs', blank=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() @@ -116,7 +113,6 @@ class RouteTarget(PrimaryModel): blank=True, null=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/netbox/models.py b/netbox/netbox/models.py index c36586f36..ce5424817 100644 --- a/netbox/netbox/models.py +++ b/netbox/netbox/models.py @@ -5,6 +5,7 @@ from django.core.serializers.json import DjangoJSONEncoder from django.core.validators import ValidationError from django.db import models from mptt.models import MPTTModel, TreeForeignKey +from taggit.managers import TaggableManager from extras.choices import ObjectChangeActionChoices from utilities.mptt import TreeManager @@ -148,8 +149,7 @@ class PrimaryModel(ChangeLoggingMixin, CustomFieldsMixin, BigIDModel): """ Primary models represent real objects within the infrastructure being modeled. """ - # TODO - # tags = TaggableManager(through=TaggedItem) + tags = TaggableManager(through='extras.TaggedItem') class Meta: abstract = True diff --git a/netbox/secrets/models.py b/netbox/secrets/models.py index 091b67cc6..932242060 100644 --- a/netbox/secrets/models.py +++ b/netbox/secrets/models.py @@ -12,9 +12,7 @@ from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse from django.utils.encoding import force_bytes -from taggit.managers import TaggableManager -from extras.models import TaggedItem from extras.utils import extras_features from netbox.models import BigIDModel, OrganizationalModel, PrimaryModel from utilities.querysets import RestrictedQuerySet @@ -312,7 +310,6 @@ class Secret(PrimaryModel): max_length=128, editable=False ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/tenancy/models.py b/netbox/tenancy/models.py index 60c945447..7d1c9dbcf 100644 --- a/netbox/tenancy/models.py +++ b/netbox/tenancy/models.py @@ -1,9 +1,7 @@ from django.db import models from django.urls import reverse from mptt.models import MPTTModel, TreeForeignKey -from taggit.managers import TaggableManager -from extras.models import TaggedItem from extras.utils import extras_features from netbox.models import NestedGroupModel, PrimaryModel from utilities.mptt import TreeManager @@ -89,7 +87,6 @@ class Tenant(PrimaryModel): comments = models.TextField( blank=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/virtualization/migrations/0020_standardize_models.py b/netbox/virtualization/migrations/0020_standardize_models.py index 42dedbc72..bea551a72 100644 --- a/netbox/virtualization/migrations/0020_standardize_models.py +++ b/netbox/virtualization/migrations/0020_standardize_models.py @@ -1,4 +1,5 @@ import django.core.serializers.json +import taggit.managers from django.db import migrations, models @@ -59,4 +60,9 @@ class Migration(migrations.Migration): name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AlterField( + model_name='vminterface', + name='tags', + field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'), + ), ] diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index 4896e26bd..32aface36 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -3,10 +3,9 @@ from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse -from taggit.managers import TaggableManager from dcim.models import BaseInterface, Device -from extras.models import ConfigContextModel, TaggedItem +from extras.models import ConfigContextModel from extras.querysets import ConfigContextModelQuerySet from extras.utils import extras_features from netbox.models import OrganizationalModel, PrimaryModel @@ -154,7 +153,6 @@ class Cluster(PrimaryModel): comments = models.TextField( blank=True ) - tags = TaggableManager(through=TaggedItem) objects = RestrictedQuerySet.as_manager() @@ -281,7 +279,6 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): object_id_field='assigned_object_id', related_query_name='virtual_machine' ) - tags = TaggableManager(through=TaggedItem) objects = ConfigContextModelQuerySet.as_manager() @@ -412,10 +409,6 @@ class VMInterface(PrimaryModel, BaseInterface): object_id_field='assigned_object_id', related_query_name='vminterface' ) - tags = TaggableManager( - through=TaggedItem, - related_name='vminterface' - ) objects = RestrictedQuerySet.as_manager() From 4397d3da021b1eb9bbcdda27dbe809ac7bceaed2 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 10 Mar 2021 14:35:44 -0500 Subject: [PATCH 5/6] Remove redundant TreeManager from TenantGroup --- netbox/tenancy/models.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/netbox/tenancy/models.py b/netbox/tenancy/models.py index 7d1c9dbcf..e5ecec639 100644 --- a/netbox/tenancy/models.py +++ b/netbox/tenancy/models.py @@ -4,7 +4,6 @@ from mptt.models import MPTTModel, TreeForeignKey from extras.utils import extras_features from netbox.models import NestedGroupModel, PrimaryModel -from utilities.mptt import TreeManager from utilities.querysets import RestrictedQuerySet @@ -40,8 +39,6 @@ class TenantGroup(NestedGroupModel): blank=True ) - objects = TreeManager() - csv_headers = ['name', 'slug', 'parent', 'description'] class Meta: From c072ba088fa0bc9ec0c1a20ad13a3688db94fab2 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 10 Mar 2021 14:49:02 -0500 Subject: [PATCH 6/6] Add webhook support for additional models --- netbox/circuits/models.py | 1 + netbox/extras/models/models.py | 1 + netbox/extras/models/tags.py | 2 ++ 3 files changed, 4 insertions(+) diff --git a/netbox/circuits/models.py b/netbox/circuits/models.py index 1ca98efd6..d19841e4f 100644 --- a/netbox/circuits/models.py +++ b/netbox/circuits/models.py @@ -231,6 +231,7 @@ class Circuit(PrimaryModel): return self._get_termination('Z') +@extras_features('webhooks') class CircuitTermination(ChangeLoggedModel, PathEndpoint, CableTermination): circuit = models.ForeignKey( to='circuits.Circuit', diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index 1fd183b8a..49dd49a42 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -379,6 +379,7 @@ class ImageAttachment(BigIDModel): # Config contexts # +@extras_features('webhooks') class ConfigContext(ChangeLoggedModel): """ A ConfigContext represents a set of arbitrary data available to any Device or VirtualMachine matching its assigned diff --git a/netbox/extras/models/tags.py b/netbox/extras/models/tags.py index d7266f359..4c61f7f88 100644 --- a/netbox/extras/models/tags.py +++ b/netbox/extras/models/tags.py @@ -2,6 +2,7 @@ from django.db import models from django.utils.text import slugify from taggit.models import TagBase, GenericTaggedItemBase +from extras.utils import extras_features from netbox.models import BigIDModel, ChangeLoggedModel from utilities.choices import ColorChoices from utilities.fields import ColorField @@ -12,6 +13,7 @@ from utilities.querysets import RestrictedQuerySet # Tags # +@extras_features('webhooks') class Tag(ChangeLoggedModel, TagBase): color = ColorField( default=ColorChoices.COLOR_GREY