From 14e23c3d00914c6c262b536fab85c26088489d46 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 4 Aug 2023 09:34:15 -0400 Subject: [PATCH] Introduce ImageAttachmentsMixin --- netbox/circuits/models/circuits.py | 10 +++------- netbox/dcim/models/devices.py | 19 ++++--------------- netbox/dcim/models/power.py | 6 ++---- netbox/dcim/models/racks.py | 6 ++---- netbox/dcim/models/sites.py | 11 +++-------- netbox/netbox/models/features.py | 13 +++++++++++++ 6 files changed, 27 insertions(+), 38 deletions(-) diff --git a/netbox/circuits/models/circuits.py b/netbox/circuits/models/circuits.py index 0643906af..e1f07b5ae 100644 --- a/netbox/circuits/models/circuits.py +++ b/netbox/circuits/models/circuits.py @@ -6,9 +6,8 @@ from django.utils.translation import gettext_lazy as _ from circuits.choices import * from dcim.models import CabledObjectModel -from netbox.models import ( - ChangeLoggedModel, CustomFieldsMixin, CustomLinksMixin, OrganizationalModel, PrimaryModel, TagsMixin, -) +from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel +from netbox.models.features import CustomFieldsMixin, CustomLinksMixin, ImageAttachmentsMixin, TagsMixin __all__ = ( 'Circuit', @@ -31,7 +30,7 @@ class CircuitType(OrganizationalModel): verbose_name_plural = _('circuit types') -class Circuit(PrimaryModel): +class Circuit(ImageAttachmentsMixin, PrimaryModel): """ A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple circuits. Each circuit is also assigned a CircuitType and a Site, and may optionally be assigned to a particular @@ -93,9 +92,6 @@ class Circuit(PrimaryModel): contacts = GenericRelation( to='tenancy.ContactAssignment' ) - images = GenericRelation( - to='extras.ImageAttachment' - ) # Cache associated CircuitTerminations termination_a = models.ForeignKey( diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index 98784ea38..e74c01f81 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -20,6 +20,7 @@ from extras.models import ConfigContextModel from extras.querysets import ConfigContextModelQuerySet from netbox.config import ConfigItem from netbox.models import OrganizationalModel, PrimaryModel +from netbox.models.features import ImageAttachmentsMixin from utilities.choices import ColorChoices from utilities.fields import ColorField, CounterCacheField, NaturalOrderingField from utilities.tracking import TrackingModelMixin @@ -62,7 +63,7 @@ class Manufacturer(OrganizationalModel): return reverse('dcim:manufacturer', args=[self.pk]) -class DeviceType(PrimaryModel, WeightMixin): +class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): """ A DeviceType represents a particular make (Manufacturer) and model of device. It specifies rack height and depth, as well as high-level functional role(s). @@ -180,10 +181,6 @@ class DeviceType(PrimaryModel, WeightMixin): to_field='device_type' ) - images = GenericRelation( - to='extras.ImageAttachment' - ) - clone_fields = ( 'manufacturer', 'default_platform', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', @@ -366,7 +363,7 @@ class DeviceType(PrimaryModel, WeightMixin): return self.subdevice_role == SubdeviceRoleChoices.ROLE_CHILD -class ModuleType(PrimaryModel, WeightMixin): +class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): """ A ModuleType represents a hardware element that can be installed within a device and which houses additional components; for example, a line card within a chassis-based switch such as the Cisco Catalyst 6500. Like a @@ -389,11 +386,6 @@ class ModuleType(PrimaryModel, WeightMixin): help_text=_('Discrete part number (optional)') ) - # Generic relations - images = GenericRelation( - to='extras.ImageAttachment' - ) - clone_fields = ('manufacturer', 'weight', 'weight_unit',) prerequisite_models = ( 'dcim.Manufacturer', @@ -539,7 +531,7 @@ def update_interface_bridges(device, interface_templates, module=None): interface.save() -class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): +class Device(ImageAttachmentsMixin, PrimaryModel, ConfigContextModel, TrackingModelMixin): """ A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType, DeviceRole, and (optionally) a Platform. Device names are not required, however if one is set it must be unique. @@ -770,9 +762,6 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): contacts = GenericRelation( to='tenancy.ContactAssignment' ) - images = GenericRelation( - to='extras.ImageAttachment' - ) objects = ConfigContextModelQuerySet.as_manager() diff --git a/netbox/dcim/models/power.py b/netbox/dcim/models/power.py index 3241ef840..1e57b6f5d 100644 --- a/netbox/dcim/models/power.py +++ b/netbox/dcim/models/power.py @@ -8,6 +8,7 @@ from django.utils.translation import gettext_lazy as _ from dcim.choices import * from netbox.config import ConfigItem from netbox.models import PrimaryModel +from netbox.models.features import ImageAttachmentsMixin from utilities.validators import ExclusionValidator from .device_components import CabledObjectModel, PathEndpoint @@ -21,7 +22,7 @@ __all__ = ( # Power # -class PowerPanel(PrimaryModel): +class PowerPanel(ImageAttachmentsMixin, PrimaryModel): """ A distribution point for electrical power; e.g. a data center RPP. """ @@ -44,9 +45,6 @@ class PowerPanel(PrimaryModel): contacts = GenericRelation( to='tenancy.ContactAssignment' ) - images = GenericRelation( - to='extras.ImageAttachment' - ) prerequisite_models = ( 'dcim.Site', diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index 12f938201..a71d798b3 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -15,6 +15,7 @@ from dcim.choices import * from dcim.constants import * from dcim.svg import RackElevationSVG from netbox.models import OrganizationalModel, PrimaryModel +from netbox.models.features import ImageAttachmentsMixin from utilities.choices import ColorChoices from utilities.fields import ColorField, NaturalOrderingField from utilities.utils import array_to_string, drange, to_grams @@ -52,7 +53,7 @@ class RackRole(OrganizationalModel): return reverse('dcim:rackrole', args=[self.pk]) -class Rack(PrimaryModel, WeightMixin): +class Rack(ImageAttachmentsMixin, PrimaryModel, WeightMixin): """ Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face. Each Rack is assigned to a Site and (optionally) a Location. @@ -196,9 +197,6 @@ class Rack(PrimaryModel, WeightMixin): contacts = GenericRelation( to='tenancy.ContactAssignment' ) - images = GenericRelation( - to='extras.ImageAttachment' - ) clone_fields = ( 'site', 'location', 'tenant', 'status', 'role', 'type', 'width', 'u_height', 'desc_units', 'outer_width', diff --git a/netbox/dcim/models/sites.py b/netbox/dcim/models/sites.py index 087b87401..87566a194 100644 --- a/netbox/dcim/models/sites.py +++ b/netbox/dcim/models/sites.py @@ -8,6 +8,7 @@ from timezone_field import TimeZoneField from dcim.choices import * from dcim.constants import * from netbox.models import NestedGroupModel, PrimaryModel +from netbox.models.features import ImageAttachmentsMixin from utilities.fields import NaturalOrderingField __all__ = ( @@ -136,7 +137,7 @@ class SiteGroup(NestedGroupModel): # Sites # -class Site(PrimaryModel): +class Site(ImageAttachmentsMixin, PrimaryModel): """ A Site represents a geographic location within a network; typically a building or campus. The optional facility field can be used to include an external designation, such as a data center name (e.g. Equinix SV6). @@ -237,9 +238,6 @@ class Site(PrimaryModel): contacts = GenericRelation( to='tenancy.ContactAssignment' ) - images = GenericRelation( - to='extras.ImageAttachment' - ) clone_fields = ( 'status', 'region', 'group', 'tenant', 'facility', 'time_zone', 'physical_address', 'shipping_address', @@ -265,7 +263,7 @@ class Site(PrimaryModel): # Locations # -class Location(NestedGroupModel): +class Location(ImageAttachmentsMixin, NestedGroupModel): """ A Location represents a subgroup of Racks and/or Devices within a Site. A Location may represent a building within a site, or a room within a building, for example. @@ -299,9 +297,6 @@ class Location(NestedGroupModel): contacts = GenericRelation( to='tenancy.ContactAssignment' ) - images = GenericRelation( - to='extras.ImageAttachment' - ) clone_fields = ('site', 'parent', 'status', 'tenant', 'description') prerequisite_models = ( diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index d5228ed63..7576ec357 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -29,6 +29,7 @@ __all__ = ( 'CustomLinksMixin', 'CustomValidationMixin', 'ExportTemplatesMixin', + 'ImageAttachmentsMixin', 'JobsMixin', 'JournalingMixin', 'SyncedDataMixin', @@ -307,6 +308,18 @@ class ExportTemplatesMixin(models.Model): abstract = True +class ImageAttachmentsMixin(models.Model): + """ + Enables the assignments of ImageAttachments. + """ + images = GenericRelation( + to='extras.ImageAttachment' + ) + + class Meta: + abstract = True + + class BookmarksMixin(models.Model): """ Enables support for user bookmarks.