Просмотр исходного кода

Support CSS class definition directly in CHOICES iterable

jeremystretch 4 лет назад
Родитель
Сommit
0d3b50a5e5

+ 6 - 15
netbox/circuits/choices.py

@@ -16,23 +16,14 @@ class CircuitStatusChoices(ChoiceSet):
     STATUS_DECOMMISSIONED = 'decommissioned'
 
     CHOICES = [
-        (STATUS_PLANNED, 'Planned'),
-        (STATUS_PROVISIONING, 'Provisioning'),
-        (STATUS_ACTIVE, 'Active'),
-        (STATUS_OFFLINE, 'Offline'),
-        (STATUS_DEPROVISIONING, 'Deprovisioning'),
-        (STATUS_DECOMMISSIONED, 'Decommissioned'),
+        (STATUS_PLANNED, 'Planned', 'info'),
+        (STATUS_PROVISIONING, 'Provisioning', 'primary'),
+        (STATUS_ACTIVE, 'Active', 'success'),
+        (STATUS_OFFLINE, 'Offline', 'danger'),
+        (STATUS_DEPROVISIONING, 'Deprovisioning', 'warning'),
+        (STATUS_DECOMMISSIONED, 'Decommissioned', 'secondary'),
     ]
 
-    CSS_CLASSES = {
-        STATUS_DEPROVISIONING: 'warning',
-        STATUS_ACTIVE: 'success',
-        STATUS_PLANNED: 'info',
-        STATUS_PROVISIONING: 'primary',
-        STATUS_OFFLINE: 'danger',
-        STATUS_DECOMMISSIONED: 'secondary',
-    }
-
 
 #
 # CircuitTerminations

+ 1 - 1
netbox/circuits/models/circuits.py

@@ -135,7 +135,7 @@ class Circuit(PrimaryModel):
         return reverse('circuits:circuit', args=[self.pk])
 
     def get_status_class(self):
-        return CircuitStatusChoices.CSS_CLASSES.get(self.status)
+        return CircuitStatusChoices.colors.get(self.status, 'secondary')
 
 
 @extras_features('webhooks')

+ 26 - 70
netbox/dcim/choices.py

@@ -15,21 +15,13 @@ class SiteStatusChoices(ChoiceSet):
     STATUS_RETIRED = 'retired'
 
     CHOICES = [
-        (STATUS_PLANNED, 'Planned'),
-        (STATUS_STAGING, 'Staging'),
-        (STATUS_ACTIVE, 'Active'),
-        (STATUS_DECOMMISSIONING, 'Decommissioning'),
-        (STATUS_RETIRED, 'Retired'),
+        (STATUS_PLANNED, 'Planned', 'info'),
+        (STATUS_STAGING, 'Staging', 'primary'),
+        (STATUS_ACTIVE, 'Active', 'primary'),
+        (STATUS_DECOMMISSIONING, 'Decommissioning', 'warning'),
+        (STATUS_RETIRED, 'Retired', 'danger'),
     ]
 
-    CSS_CLASSES = {
-        STATUS_PLANNED: 'info',
-        STATUS_STAGING: 'primary',
-        STATUS_ACTIVE: 'success',
-        STATUS_DECOMMISSIONING: 'warning',
-        STATUS_RETIRED: 'danger',
-    }
-
 
 #
 # Racks
@@ -77,21 +69,13 @@ class RackStatusChoices(ChoiceSet):
     STATUS_DEPRECATED = 'deprecated'
 
     CHOICES = [
-        (STATUS_RESERVED, 'Reserved'),
-        (STATUS_AVAILABLE, 'Available'),
-        (STATUS_PLANNED, 'Planned'),
-        (STATUS_ACTIVE, 'Active'),
-        (STATUS_DEPRECATED, 'Deprecated'),
+        (STATUS_RESERVED, 'Reserved', 'warning'),
+        (STATUS_AVAILABLE, 'Available', 'success'),
+        (STATUS_PLANNED, 'Planned', 'info'),
+        (STATUS_ACTIVE, 'Active', 'primary'),
+        (STATUS_DEPRECATED, 'Deprecated', 'danger'),
     ]
 
-    CSS_CLASSES = {
-        STATUS_RESERVED: 'warning',
-        STATUS_AVAILABLE: 'success',
-        STATUS_PLANNED: 'info',
-        STATUS_ACTIVE: 'primary',
-        STATUS_DEPRECATED: 'danger',
-    }
-
 
 class RackDimensionUnitChoices(ChoiceSet):
 
@@ -157,25 +141,15 @@ class DeviceStatusChoices(ChoiceSet):
     STATUS_DECOMMISSIONING = 'decommissioning'
 
     CHOICES = [
-        (STATUS_OFFLINE, 'Offline'),
-        (STATUS_ACTIVE, 'Active'),
-        (STATUS_PLANNED, 'Planned'),
-        (STATUS_STAGED, 'Staged'),
-        (STATUS_FAILED, 'Failed'),
-        (STATUS_INVENTORY, 'Inventory'),
-        (STATUS_DECOMMISSIONING, 'Decommissioning'),
+        (STATUS_OFFLINE, 'Offline', 'warning'),
+        (STATUS_ACTIVE, 'Active', 'success'),
+        (STATUS_PLANNED, 'Planned', 'info'),
+        (STATUS_STAGED, 'Staged', 'primary'),
+        (STATUS_FAILED, 'Failed', 'danger'),
+        (STATUS_INVENTORY, 'Inventory', 'secondary'),
+        (STATUS_DECOMMISSIONING, 'Decommissioning', 'warning'),
     ]
 
-    CSS_CLASSES = {
-        STATUS_OFFLINE: 'warning',
-        STATUS_ACTIVE: 'success',
-        STATUS_PLANNED: 'info',
-        STATUS_STAGED: 'primary',
-        STATUS_FAILED: 'danger',
-        STATUS_INVENTORY: 'secondary',
-        STATUS_DECOMMISSIONING: 'warning',
-    }
-
 
 class DeviceAirflowChoices(ChoiceSet):
 
@@ -1147,17 +1121,11 @@ class LinkStatusChoices(ChoiceSet):
     STATUS_DECOMMISSIONING = 'decommissioning'
 
     CHOICES = (
-        (STATUS_CONNECTED, 'Connected'),
-        (STATUS_PLANNED, 'Planned'),
-        (STATUS_DECOMMISSIONING, 'Decommissioning'),
+        (STATUS_CONNECTED, 'Connected', 'success'),
+        (STATUS_PLANNED, 'Planned', 'info'),
+        (STATUS_DECOMMISSIONING, 'Decommissioning', 'warning'),
     )
 
-    CSS_CLASSES = {
-        STATUS_CONNECTED: 'success',
-        STATUS_PLANNED: 'info',
-        STATUS_DECOMMISSIONING: 'warning',
-    }
-
 
 class CableLengthUnitChoices(ChoiceSet):
 
@@ -1194,19 +1162,12 @@ class PowerFeedStatusChoices(ChoiceSet):
     STATUS_FAILED = 'failed'
 
     CHOICES = [
-        (STATUS_OFFLINE, 'Offline'),
-        (STATUS_ACTIVE, 'Active'),
-        (STATUS_PLANNED, 'Planned'),
-        (STATUS_FAILED, 'Failed'),
+        (STATUS_OFFLINE, 'Offline', 'warning'),
+        (STATUS_ACTIVE, 'Active', 'success'),
+        (STATUS_PLANNED, 'Planned', 'info'),
+        (STATUS_FAILED, 'Failed', 'danger'),
     ]
 
-    CSS_CLASSES = {
-        STATUS_OFFLINE: 'warning',
-        STATUS_ACTIVE: 'success',
-        STATUS_PLANNED: 'info',
-        STATUS_FAILED: 'danger',
-    }
-
 
 class PowerFeedTypeChoices(ChoiceSet):
 
@@ -1214,15 +1175,10 @@ class PowerFeedTypeChoices(ChoiceSet):
     TYPE_REDUNDANT = 'redundant'
 
     CHOICES = (
-        (TYPE_PRIMARY, 'Primary'),
-        (TYPE_REDUNDANT, 'Redundant'),
+        (TYPE_PRIMARY, 'Primary', 'success'),
+        (TYPE_REDUNDANT, 'Redundant', 'info'),
     )
 
-    CSS_CLASSES = {
-        TYPE_PRIMARY: 'success',
-        TYPE_REDUNDANT: 'info',
-    }
-
 
 class PowerFeedSupplyChoices(ChoiceSet):
 

+ 1 - 1
netbox/dcim/models/cables.py

@@ -289,7 +289,7 @@ class Cable(PrimaryModel):
         self._pk = self.pk
 
     def get_status_class(self):
-        return LinkStatusChoices.CSS_CLASSES.get(self.status)
+        return LinkStatusChoices.colors.get(self.status)
 
     def get_compatible_types(self):
         """

+ 1 - 1
netbox/dcim/models/devices.py

@@ -862,7 +862,7 @@ class Device(PrimaryModel, ConfigContextModel):
         return Device.objects.filter(parent_bay__device=self.pk)
 
     def get_status_class(self):
-        return DeviceStatusChoices.CSS_CLASSES.get(self.status)
+        return DeviceStatusChoices.colors.get(self.status, 'secondary')
 
 
 #

+ 2 - 2
netbox/dcim/models/power.py

@@ -174,7 +174,7 @@ class PowerFeed(PrimaryModel, PathEndpoint, LinkTermination):
         return self.power_panel
 
     def get_type_class(self):
-        return PowerFeedTypeChoices.CSS_CLASSES.get(self.type)
+        return PowerFeedTypeChoices.colors.get(self.type)
 
     def get_status_class(self):
-        return PowerFeedStatusChoices.CSS_CLASSES.get(self.status)
+        return PowerFeedStatusChoices.colors.get(self.status, 'secondary')

+ 1 - 1
netbox/dcim/models/racks.py

@@ -251,7 +251,7 @@ class Rack(PrimaryModel):
             return reversed(range(1, self.u_height + 1))
 
     def get_status_class(self):
-        return RackStatusChoices.CSS_CLASSES.get(self.status)
+        return RackStatusChoices.colors.get(self.status, 'secondary')
 
     def get_rack_units(self, user=None, face=DeviceFaceChoices.FACE_FRONT, exclude=None, expand_devices=True):
         """

+ 1 - 1
netbox/dcim/models/sites.py

@@ -315,7 +315,7 @@ class Site(PrimaryModel):
         return reverse('dcim:site', args=[self.pk])
 
     def get_status_class(self):
-        return SiteStatusChoices.CSS_CLASSES.get(self.status)
+        return SiteStatusChoices.colors.get(self.status, 'secondary')
 
 
 #

+ 12 - 33
netbox/extras/choices.py

@@ -91,17 +91,11 @@ class ObjectChangeActionChoices(ChoiceSet):
     ACTION_DELETE = 'delete'
 
     CHOICES = (
-        (ACTION_CREATE, 'Created'),
-        (ACTION_UPDATE, 'Updated'),
-        (ACTION_DELETE, 'Deleted'),
+        (ACTION_CREATE, 'Created', 'success'),
+        (ACTION_UPDATE, 'Updated', 'primary'),
+        (ACTION_DELETE, 'Deleted', 'danger'),
     )
 
-    CSS_CLASSES = {
-        ACTION_CREATE: 'success',
-        ACTION_UPDATE: 'primary',
-        ACTION_DELETE: 'danger',
-    }
-
 
 #
 # Jounral entries
@@ -115,19 +109,12 @@ class JournalEntryKindChoices(ChoiceSet):
     KIND_DANGER = 'danger'
 
     CHOICES = (
-        (KIND_INFO, 'Info'),
-        (KIND_SUCCESS, 'Success'),
-        (KIND_WARNING, 'Warning'),
-        (KIND_DANGER, 'Danger'),
+        (KIND_INFO, 'Info', 'info'),
+        (KIND_SUCCESS, 'Success', 'success'),
+        (KIND_WARNING, 'Warning', 'warning'),
+        (KIND_DANGER, 'Danger', 'danger'),
     )
 
-    CSS_CLASSES = {
-        KIND_INFO: 'info',
-        KIND_SUCCESS: 'success',
-        KIND_WARNING: 'warning',
-        KIND_DANGER: 'danger',
-    }
-
 
 #
 # Log Levels for Reports and Scripts
@@ -142,21 +129,13 @@ class LogLevelChoices(ChoiceSet):
     LOG_FAILURE = 'failure'
 
     CHOICES = (
-        (LOG_DEFAULT, 'Default'),
-        (LOG_SUCCESS, 'Success'),
-        (LOG_INFO, 'Info'),
-        (LOG_WARNING, 'Warning'),
-        (LOG_FAILURE, 'Failure'),
+        (LOG_DEFAULT, 'Default', 'secondary'),
+        (LOG_SUCCESS, 'Success', 'success'),
+        (LOG_INFO, 'Info', 'info'),
+        (LOG_WARNING, 'Warning', 'warning'),
+        (LOG_FAILURE, 'Failure', 'danger'),
     )
 
-    CSS_CLASSES = {
-        LOG_DEFAULT: 'secondary',
-        LOG_SUCCESS: 'success',
-        LOG_INFO: 'info',
-        LOG_WARNING: 'warning',
-        LOG_FAILURE: 'danger',
-    }
-
 
 #
 # Job results

+ 1 - 1
netbox/extras/models/change_logging.py

@@ -105,4 +105,4 @@ class ObjectChange(BigIDModel):
         return reverse('extras:objectchange', args=[self.pk])
 
     def get_action_class(self):
-        return ObjectChangeActionChoices.CSS_CLASSES.get(self.action)
+        return ObjectChangeActionChoices.colors.get(self.action)

+ 1 - 1
netbox/extras/models/models.py

@@ -440,7 +440,7 @@ class JournalEntry(ChangeLoggedModel):
         return reverse('extras:journalentry', args=[self.pk])
 
     def get_kind_class(self):
-        return JournalEntryKindChoices.CSS_CLASSES.get(self.kind)
+        return JournalEntryKindChoices.colors.get(self.kind)
 
 
 class JobResult(BigIDModel):

+ 1 - 1
netbox/extras/templatetags/log_levels.py

@@ -13,5 +13,5 @@ def log_level(level):
     """
     return {
         'name': LogLevelChoices.as_dict()[level],
-        'class': LogLevelChoices.CSS_CLASSES.get(level)
+        'class': LogLevelChoices.colors.get(level)
     }

+ 22 - 60
netbox/ipam/choices.py

@@ -25,19 +25,12 @@ class PrefixStatusChoices(ChoiceSet):
     STATUS_DEPRECATED = 'deprecated'
 
     CHOICES = [
-        (STATUS_CONTAINER, 'Container'),
-        (STATUS_ACTIVE, 'Active'),
-        (STATUS_RESERVED, 'Reserved'),
-        (STATUS_DEPRECATED, 'Deprecated'),
+        (STATUS_CONTAINER, 'Container', 'secondary'),
+        (STATUS_ACTIVE, 'Active', 'primary'),
+        (STATUS_RESERVED, 'Reserved', 'info'),
+        (STATUS_DEPRECATED, 'Deprecated', 'danger'),
     ]
 
-    CSS_CLASSES = {
-        STATUS_CONTAINER: 'secondary',
-        STATUS_ACTIVE: 'primary',
-        STATUS_RESERVED: 'info',
-        STATUS_DEPRECATED: 'danger',
-    }
-
 
 #
 # IP Ranges
@@ -51,17 +44,11 @@ class IPRangeStatusChoices(ChoiceSet):
     STATUS_DEPRECATED = 'deprecated'
 
     CHOICES = [
-        (STATUS_ACTIVE, 'Active'),
-        (STATUS_RESERVED, 'Reserved'),
-        (STATUS_DEPRECATED, 'Deprecated'),
+        (STATUS_ACTIVE, 'Active', 'primary'),
+        (STATUS_RESERVED, 'Reserved', 'info'),
+        (STATUS_DEPRECATED, 'Deprecated', 'danger'),
     ]
 
-    CSS_CLASSES = {
-        STATUS_ACTIVE: 'primary',
-        STATUS_RESERVED: 'info',
-        STATUS_DEPRECATED: 'danger',
-    }
-
 
 #
 # IP Addresses
@@ -77,21 +64,13 @@ class IPAddressStatusChoices(ChoiceSet):
     STATUS_SLAAC = 'slaac'
 
     CHOICES = [
-        (STATUS_ACTIVE, 'Active'),
-        (STATUS_RESERVED, 'Reserved'),
-        (STATUS_DEPRECATED, 'Deprecated'),
-        (STATUS_DHCP, 'DHCP'),
-        (STATUS_SLAAC, 'SLAAC'),
+        (STATUS_ACTIVE, 'Active', 'primary'),
+        (STATUS_RESERVED, 'Reserved', 'info'),
+        (STATUS_DEPRECATED, 'Deprecated', 'danger'),
+        (STATUS_DHCP, 'DHCP', 'success'),
+        (STATUS_SLAAC, 'SLAAC', 'success'),
     ]
 
-    CSS_CLASSES = {
-        STATUS_ACTIVE: 'primary',
-        STATUS_RESERVED: 'info',
-        STATUS_DEPRECATED: 'danger',
-        STATUS_DHCP: 'success',
-        STATUS_SLAAC: 'success',
-    }
-
 
 class IPAddressRoleChoices(ChoiceSet):
 
@@ -105,27 +84,16 @@ class IPAddressRoleChoices(ChoiceSet):
     ROLE_CARP = 'carp'
 
     CHOICES = (
-        (ROLE_LOOPBACK, 'Loopback'),
-        (ROLE_SECONDARY, 'Secondary'),
-        (ROLE_ANYCAST, 'Anycast'),
+        (ROLE_LOOPBACK, 'Loopback', 'secondary'),
+        (ROLE_SECONDARY, 'Secondary', 'primary'),
+        (ROLE_ANYCAST, 'Anycast', 'warning'),
         (ROLE_VIP, 'VIP'),
-        (ROLE_VRRP, 'VRRP'),
-        (ROLE_HSRP, 'HSRP'),
-        (ROLE_GLBP, 'GLBP'),
-        (ROLE_CARP, 'CARP'),
+        (ROLE_VRRP, 'VRRP', 'success'),
+        (ROLE_HSRP, 'HSRP', 'success'),
+        (ROLE_GLBP, 'GLBP', 'success'),
+        (ROLE_CARP, 'CARP'), 'success',
     )
 
-    CSS_CLASSES = {
-        ROLE_LOOPBACK: 'secondary',
-        ROLE_SECONDARY: 'primary',
-        ROLE_ANYCAST: 'warning',
-        ROLE_VIP: 'success',
-        ROLE_VRRP: 'success',
-        ROLE_HSRP: 'success',
-        ROLE_GLBP: 'success',
-        ROLE_CARP: 'success',
-    }
-
 
 #
 # FHRP
@@ -171,17 +139,11 @@ class VLANStatusChoices(ChoiceSet):
     STATUS_DEPRECATED = 'deprecated'
 
     CHOICES = [
-        (STATUS_ACTIVE, 'Active'),
-        (STATUS_RESERVED, 'Reserved'),
-        (STATUS_DEPRECATED, 'Deprecated'),
+        (STATUS_ACTIVE, 'Active', 'primary'),
+        (STATUS_RESERVED, 'Reserved', 'info'),
+        (STATUS_DEPRECATED, 'Deprecated', 'danger'),
     ]
 
-    CSS_CLASSES = {
-        STATUS_ACTIVE: 'primary',
-        STATUS_RESERVED: 'info',
-        STATUS_DEPRECATED: 'danger',
-    }
-
 
 #
 # Services

+ 4 - 4
netbox/ipam/models/ip.py

@@ -403,7 +403,7 @@ class Prefix(PrimaryModel):
     prefix_length = property(fset=_set_prefix_length)
 
     def get_status_class(self):
-        return PrefixStatusChoices.CSS_CLASSES.get(self.status)
+        return PrefixStatusChoices.colors.get(self.status, 'secondary')
 
     def get_parents(self, include_self=False):
         """
@@ -692,7 +692,7 @@ class IPRange(PrimaryModel):
     prefix_length = property(fset=_set_prefix_length)
 
     def get_status_class(self):
-        return IPRangeStatusChoices.CSS_CLASSES.get(self.status)
+        return IPRangeStatusChoices.colors.get(self.status, 'secondary')
 
     def get_child_ips(self):
         """
@@ -909,7 +909,7 @@ class IPAddress(PrimaryModel):
     mask_length = property(fset=_set_mask_length)
 
     def get_status_class(self):
-        return IPAddressStatusChoices.CSS_CLASSES.get(self.status)
+        return IPAddressStatusChoices.colors.get(self.status, 'secondary')
 
     def get_role_class(self):
-        return IPAddressRoleChoices.CSS_CLASSES.get(self.role)
+        return IPAddressRoleChoices.colors.get(self.role)

+ 1 - 1
netbox/ipam/models/vlans.py

@@ -173,7 +173,7 @@ class VLAN(PrimaryModel):
             })
 
     def get_status_class(self):
-        return VLANStatusChoices.CSS_CLASSES.get(self.status)
+        return VLANStatusChoices.colors.get(self.status, 'secondary')
 
     def get_interfaces(self):
         # Return all device interfaces assigned to this VLAN

+ 21 - 4
netbox/utilities/choices.py

@@ -8,20 +8,37 @@ class ChoiceSetMeta(type):
     def __new__(mcs, name, bases, attrs):
 
         # Extend static choices with any configured choices
-        if 'key' in attrs:
+        key = attrs.get('key')
+        if key:
             try:
-                attrs['CHOICES'].extend(settings.FIELD_CHOICES[attrs['key']])
+                attrs['CHOICES'].extend(settings.FIELD_CHOICES[key])
             except KeyError:
                 pass
 
+        # Define choice tuples
+        # TODO: Support optgroup nesting
+        attrs['_choices'] = [
+            (c[0], c[1]) for c in attrs['CHOICES']
+        ]
+
+        # Define color maps
+        # TODO: Support optgroup nesting
+        colors = {}
+        for c in attrs['CHOICES']:
+            try:
+                colors[c[0]] = c[2]
+            except IndexError:
+                pass
+        attrs['colors'] = colors
+
         return super().__new__(mcs, name, bases, attrs)
 
     def __call__(cls, *args, **kwargs):
         # Django will check if a 'choices' value is callable, and if so assume that it returns an iterable
-        return getattr(cls, 'CHOICES', ())
+        return getattr(cls, '_choices', ())
 
     def __iter__(cls):
-        choices = getattr(cls, 'CHOICES', ())
+        choices = getattr(cls, '_choices', ())
         return iter(choices)
 
 

+ 6 - 15
netbox/virtualization/choices.py

@@ -16,19 +16,10 @@ class VirtualMachineStatusChoices(ChoiceSet):
     STATUS_DECOMMISSIONING = 'decommissioning'
 
     CHOICES = [
-        (STATUS_OFFLINE, 'Offline'),
-        (STATUS_ACTIVE, 'Active'),
-        (STATUS_PLANNED, 'Planned'),
-        (STATUS_STAGED, 'Staged'),
-        (STATUS_FAILED, 'Failed'),
-        (STATUS_DECOMMISSIONING, 'Decommissioning'),
+        (STATUS_OFFLINE, 'Offline', 'warning'),
+        (STATUS_ACTIVE, 'Active', 'success'),
+        (STATUS_PLANNED, 'Planned', 'info'),
+        (STATUS_STAGED, 'Staged', 'primary'),
+        (STATUS_FAILED, 'Failed', 'danger'),
+        (STATUS_DECOMMISSIONING, 'Decommissioning', 'warning'),
     ]
-
-    CSS_CLASSES = {
-        STATUS_OFFLINE: 'warning',
-        STATUS_ACTIVE: 'success',
-        STATUS_PLANNED: 'info',
-        STATUS_STAGED: 'primary',
-        STATUS_FAILED: 'danger',
-        STATUS_DECOMMISSIONING: 'warning',
-    }

+ 1 - 1
netbox/virtualization/models.py

@@ -329,7 +329,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
                     })
 
     def get_status_class(self):
-        return VirtualMachineStatusChoices.CSS_CLASSES.get(self.status)
+        return VirtualMachineStatusChoices.colors.get(self.status, 'secondary')
 
     @property
     def primary_ip(self):

+ 1 - 1
netbox/wireless/models.py

@@ -182,7 +182,7 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
         return reverse('wireless:wirelesslink', args=[self.pk])
 
     def get_status_class(self):
-        return LinkStatusChoices.CSS_CLASSES.get(self.status)
+        return LinkStatusChoices.colors.get(self.status)
 
     def clean(self):