From 8cc968532225b918c5b9b7b767fd290970a08669 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 1 Mar 2021 15:42:39 -0500 Subject: [PATCH] Closes #5901: Add 'created' and 'last_updated' fields to device component models --- docs/release-notes/version-2.11.md | 1 + netbox/dcim/api/serializers.py | 48 +++-- .../migrations/0123_standardize_models.py | 170 ++++++++++++++++++ .../dcim/models/device_component_templates.py | 4 +- netbox/dcim/models/device_components.py | 4 +- netbox/netbox/models.py | 1 + netbox/templates/dcim/device_component.html | 1 + 7 files changed, 210 insertions(+), 19 deletions(-) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 20ca17b4e..7acc24cc5 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -9,6 +9,7 @@ * [#5370](https://github.com/netbox-community/netbox/issues/5370) - Extend custom field support to organizational models * [#5401](https://github.com/netbox-community/netbox/issues/5401) - Extend custom field support to device component models * [#5451](https://github.com/netbox-community/netbox/issues/5451) - Add support for multiple-selection custom fields +* [#5901](https://github.com/netbox-community/netbox/issues/5901) - Add `created` and `last_updated` fields to device component models ### Other Changes diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index d94d3aa49..363687d1e 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -288,7 +288,7 @@ class ConsolePortTemplateSerializer(ValidatedModelSerializer): class Meta: model = ConsolePortTemplate - fields = ['id', 'url', 'device_type', 'name', 'label', 'type', 'description'] + fields = ['id', 'url', 'device_type', 'name', 'label', 'type', 'description', 'created', 'last_updated'] class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer): @@ -302,7 +302,7 @@ class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer): class Meta: model = ConsoleServerPortTemplate - fields = ['id', 'url', 'device_type', 'name', 'label', 'type', 'description'] + fields = ['id', 'url', 'device_type', 'name', 'label', 'type', 'description', 'created', 'last_updated'] class PowerPortTemplateSerializer(ValidatedModelSerializer): @@ -316,7 +316,10 @@ class PowerPortTemplateSerializer(ValidatedModelSerializer): class Meta: model = PowerPortTemplate - fields = ['id', 'url', 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description'] + fields = [ + 'id', 'url', 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', + 'created', 'last_updated', + ] class PowerOutletTemplateSerializer(ValidatedModelSerializer): @@ -338,7 +341,10 @@ class PowerOutletTemplateSerializer(ValidatedModelSerializer): class Meta: model = PowerOutletTemplate - fields = ['id', 'url', 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description'] + fields = [ + 'id', 'url', 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'created', + 'last_updated', + ] class InterfaceTemplateSerializer(ValidatedModelSerializer): @@ -348,7 +354,9 @@ class InterfaceTemplateSerializer(ValidatedModelSerializer): class Meta: model = InterfaceTemplate - fields = ['id', 'url', 'device_type', 'name', 'label', 'type', 'mgmt_only', 'description'] + fields = [ + 'id', 'url', 'device_type', 'name', 'label', 'type', 'mgmt_only', 'description', 'created', 'last_updated', + ] class RearPortTemplateSerializer(ValidatedModelSerializer): @@ -358,7 +366,9 @@ class RearPortTemplateSerializer(ValidatedModelSerializer): class Meta: model = RearPortTemplate - fields = ['id', 'url', 'device_type', 'name', 'label', 'type', 'positions', 'description'] + fields = [ + 'id', 'url', 'device_type', 'name', 'label', 'type', 'positions', 'description', 'created', 'last_updated', + ] class FrontPortTemplateSerializer(ValidatedModelSerializer): @@ -369,7 +379,10 @@ class FrontPortTemplateSerializer(ValidatedModelSerializer): class Meta: model = FrontPortTemplate - fields = ['id', 'url', 'device_type', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description'] + fields = [ + 'id', 'url', 'device_type', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', + 'created', 'last_updated', + ] class DeviceBayTemplateSerializer(ValidatedModelSerializer): @@ -378,7 +391,7 @@ class DeviceBayTemplateSerializer(ValidatedModelSerializer): class Meta: model = DeviceBayTemplate - fields = ['id', 'url', 'device_type', 'name', 'label', 'description'] + fields = ['id', 'url', 'device_type', 'name', 'label', 'description', 'created', 'last_updated'] # @@ -498,6 +511,7 @@ class ConsoleServerPortSerializer(TaggedObjectSerializer, CableTerminationSerial fields = [ 'id', 'url', 'device', 'name', 'label', 'type', 'description', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', + 'created', 'last_updated', ] @@ -516,6 +530,7 @@ class ConsolePortSerializer(TaggedObjectSerializer, CableTerminationSerializer, fields = [ 'id', 'url', 'device', 'name', 'label', 'type', 'description', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', + 'created', 'last_updated', ] @@ -544,7 +559,7 @@ class PowerOutletSerializer(TaggedObjectSerializer, CableTerminationSerializer, fields = [ 'id', 'url', 'device', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type', - 'connected_endpoint_reachable', 'tags', 'custom_fields', + 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', ] @@ -563,7 +578,7 @@ class PowerPortSerializer(TaggedObjectSerializer, CableTerminationSerializer, Co fields = [ 'id', 'url', 'device', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type', - 'connected_endpoint_reachable', 'tags', 'custom_fields', + 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', ] @@ -589,7 +604,7 @@ class InterfaceSerializer(TaggedObjectSerializer, CableTerminationSerializer, Co 'id', 'url', 'device', 'name', 'label', 'type', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only', 'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', - 'count_ipaddresses', + 'created', 'last_updated', 'count_ipaddresses', ] def validate(self, data): @@ -616,7 +631,7 @@ class RearPortSerializer(TaggedObjectSerializer, CableTerminationSerializer, Cus model = RearPort fields = [ 'id', 'url', 'device', 'name', 'label', 'type', 'positions', 'description', 'cable', 'cable_peer', - 'cable_peer_type', 'tags', 'custom_fields', + 'cable_peer_type', 'tags', 'custom_fields', 'created', 'last_updated', ] @@ -642,7 +657,7 @@ class FrontPortSerializer(TaggedObjectSerializer, CableTerminationSerializer, Cu model = FrontPort fields = [ 'id', 'url', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'cable', - 'cable_peer', 'cable_peer_type', 'tags', 'custom_fields', + 'cable_peer', 'cable_peer_type', 'tags', 'custom_fields', 'created', 'last_updated', ] @@ -653,7 +668,10 @@ class DeviceBaySerializer(TaggedObjectSerializer, CustomFieldModelSerializer): class Meta: model = DeviceBay - fields = ['id', 'url', 'device', 'name', 'label', 'description', 'installed_device', 'tags', 'custom_fields'] + fields = [ + 'id', 'url', 'device', 'name', 'label', 'description', 'installed_device', 'tags', 'custom_fields', + 'created', 'last_updated', + ] # @@ -672,7 +690,7 @@ class InventoryItemSerializer(TaggedObjectSerializer, CustomFieldModelSerializer model = InventoryItem fields = [ 'id', 'url', 'device', 'parent', 'name', 'label', 'manufacturer', 'part_id', 'serial', 'asset_tag', - 'discovered', 'description', 'tags', 'custom_fields', '_depth', + 'discovered', 'description', 'tags', 'custom_fields', 'created', 'last_updated', '_depth', ] diff --git a/netbox/dcim/migrations/0123_standardize_models.py b/netbox/dcim/migrations/0123_standardize_models.py index ca8534559..48e9e6503 100644 --- a/netbox/dcim/migrations/0123_standardize_models.py +++ b/netbox/dcim/migrations/0123_standardize_models.py @@ -9,41 +9,151 @@ class Migration(migrations.Migration): ] operations = [ + migrations.AddField( + model_name='consoleport', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), migrations.AddField( model_name='consoleport', name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='consoleport', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='consoleporttemplate', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), + migrations.AddField( + model_name='consoleporttemplate', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='consoleserverport', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), migrations.AddField( model_name='consoleserverport', name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='consoleserverport', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='consoleserverporttemplate', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), + migrations.AddField( + model_name='consoleserverporttemplate', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='devicebay', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), migrations.AddField( model_name='devicebay', name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='devicebay', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='devicebaytemplate', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), + migrations.AddField( + model_name='devicebaytemplate', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), migrations.AddField( model_name='devicerole', name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='frontport', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), migrations.AddField( model_name='frontport', name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='frontport', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='frontporttemplate', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), + migrations.AddField( + model_name='frontporttemplate', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='interface', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), migrations.AddField( model_name='interface', name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='interface', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='interfacetemplate', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), + migrations.AddField( + model_name='interfacetemplate', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='inventoryitem', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), migrations.AddField( model_name='inventoryitem', name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='inventoryitem', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), migrations.AddField( model_name='manufacturer', name='custom_field_data', @@ -54,16 +164,56 @@ class Migration(migrations.Migration): name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='poweroutlet', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), migrations.AddField( model_name='poweroutlet', name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='poweroutlet', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='poweroutlettemplate', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), + migrations.AddField( + model_name='poweroutlettemplate', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='powerport', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), migrations.AddField( model_name='powerport', name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='powerport', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='powerporttemplate', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), + migrations.AddField( + model_name='powerporttemplate', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), migrations.AddField( model_name='rackgroup', name='custom_field_data', @@ -74,11 +224,31 @@ class Migration(migrations.Migration): name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='rearport', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), migrations.AddField( model_name='rearport', name='custom_field_data', field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), ), + migrations.AddField( + model_name='rearport', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), + migrations.AddField( + model_name='rearporttemplate', + name='created', + field=models.DateField(auto_now_add=True, null=True), + ), + migrations.AddField( + model_name='rearporttemplate', + name='last_updated', + field=models.DateTimeField(auto_now=True, null=True), + ), migrations.AddField( model_name='region', name='custom_field_data', diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index d250227b7..51956f924 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -6,7 +6,7 @@ from dcim.choices import * from dcim.constants import * from extras.models import ObjectChange from extras.utils import extras_features -from netbox.models import BigIDModel +from netbox.models import BigIDModel, ChangeLoggingMixin from utilities.fields import NaturalOrderingField from utilities.querysets import RestrictedQuerySet from utilities.ordering import naturalize_interface @@ -28,7 +28,7 @@ __all__ = ( ) -class ComponentTemplateModel(BigIDModel): +class ComponentTemplateModel(ChangeLoggingMixin, BigIDModel): device_type = models.ForeignKey( to='dcim.DeviceType', on_delete=models.CASCADE, diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 10422a59b..f78363ba9 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -13,7 +13,7 @@ from dcim.constants import * from dcim.fields import MACAddressField from extras.models import ObjectChange, TaggedItem from extras.utils import extras_features -from netbox.models import BigIDModel, CustomFieldsMixin +from netbox.models import PrimaryModel from utilities.fields import NaturalOrderingField from utilities.mptt import TreeManager from utilities.ordering import naturalize_interface @@ -38,7 +38,7 @@ __all__ = ( ) -class ComponentModel(CustomFieldsMixin, BigIDModel): +class ComponentModel(PrimaryModel): """ An abstract model inherited by any model which has a parent Device. """ diff --git a/netbox/netbox/models.py b/netbox/netbox/models.py index 3b97d4ef0..63965d4e6 100644 --- a/netbox/netbox/models.py +++ b/netbox/netbox/models.py @@ -10,6 +10,7 @@ from utilities.utils import serialize_object __all__ = ( 'BigIDModel', + 'ChangeLoggingMixin', 'CustomFieldsMixin', 'NestedGroupModel', 'OrganizationalModel', diff --git a/netbox/templates/dcim/device_component.html b/netbox/templates/dcim/device_component.html index c0ce453b3..ecd42d796 100644 --- a/netbox/templates/dcim/device_component.html +++ b/netbox/templates/dcim/device_component.html @@ -30,6 +30,7 @@ {% endif %}

{% block title %}{{ object.device }} / {{ object }}{% endblock %}

+ {% include 'inc/created_updated.html' %}