From 9d2437c3b7c1cd31ba961e3cf7418802a491f8f5 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 31 Jul 2023 11:00:59 -0400 Subject: [PATCH] Misc cleanup, capitalization fixes --- netbox/circuits/models/circuits.py | 18 +-- netbox/circuits/models/providers.py | 4 +- netbox/circuits/tables/circuits.py | 9 +- netbox/core/models/data.py | 3 +- netbox/core/models/jobs.py | 2 +- netbox/dcim/models/cables.py | 2 +- .../dcim/models/device_component_templates.py | 15 ++- netbox/dcim/models/device_components.py | 118 ++++++++++------- netbox/dcim/models/devices.py | 120 +++++++++++------- netbox/dcim/models/mixins.py | 2 +- netbox/dcim/models/racks.py | 16 +-- netbox/dcim/models/sites.py | 5 +- netbox/extras/models/change_logging.py | 4 +- netbox/extras/models/configs.py | 2 +- netbox/extras/models/customfields.py | 76 +++++++---- netbox/extras/models/models.py | 67 ++++++---- netbox/ipam/models/asns.py | 6 +- netbox/ipam/models/fhrp.py | 6 +- netbox/ipam/models/ip.py | 54 +++++--- netbox/ipam/models/l2vpn.py | 13 +- netbox/ipam/models/services.py | 5 +- netbox/ipam/models/vlans.py | 16 ++- netbox/ipam/models/vrfs.py | 4 +- netbox/netbox/models/__init__.py | 2 +- netbox/netbox/models/features.py | 1 - netbox/users/models.py | 12 +- .../virtualization/models/virtualmachines.py | 45 ++++--- netbox/wireless/models.py | 21 +-- 28 files changed, 394 insertions(+), 254 deletions(-) diff --git a/netbox/circuits/models/circuits.py b/netbox/circuits/models/circuits.py index 667eab1cb..af8b5f042 100644 --- a/netbox/circuits/models/circuits.py +++ b/netbox/circuits/models/circuits.py @@ -34,7 +34,7 @@ class Circuit(PrimaryModel): """ cid = models.CharField( max_length=100, - verbose_name=_('Circuit ID'), + verbose_name=_('circuit ID'), help_text=_('Unique circuit ID') ) provider = models.ForeignKey( @@ -70,17 +70,17 @@ class Circuit(PrimaryModel): install_date = models.DateField( blank=True, null=True, - verbose_name=_('Installed') + verbose_name=_('installed') ) termination_date = models.DateField( blank=True, null=True, - verbose_name=_('Terminates') + verbose_name=_('terminates') ) commit_rate = models.PositiveIntegerField( blank=True, null=True, - verbose_name=_('Commit rate (Kbps)'), + verbose_name=_('commit rate (Kbps)'), help_text=_("Committed rate") ) @@ -163,7 +163,7 @@ class CircuitTermination( term_side = models.CharField( max_length=1, choices=CircuitTerminationSideChoices, - verbose_name=_('Termination') + verbose_name=_('termination') ) site = models.ForeignKey( to='dcim.Site', @@ -180,7 +180,7 @@ class CircuitTermination( null=True ) port_speed = models.PositiveIntegerField( - verbose_name=_('Port speed (Kbps)'), + verbose_name=_('port speed (Kbps)'), blank=True, null=True, help_text=_('Physical circuit speed') @@ -188,19 +188,19 @@ class CircuitTermination( upstream_speed = models.PositiveIntegerField( blank=True, null=True, - verbose_name=_('Upstream speed (Kbps)'), + verbose_name=_('upstream speed (Kbps)'), help_text=_('Upstream speed, if different from port speed') ) xconnect_id = models.CharField( max_length=50, blank=True, - verbose_name=_('Cross-connect ID'), + verbose_name=_('cross-connect ID'), help_text=_('ID of the local cross-connect') ) pp_info = models.CharField( max_length=100, blank=True, - verbose_name=_('Patch panel/port(s)'), + verbose_name=_('patch panel/port(s)'), help_text=_('Patch panel ID and port number(s)') ) description = models.CharField( diff --git a/netbox/circuits/models/providers.py b/netbox/circuits/models/providers.py index de946798f..e28a82a79 100644 --- a/netbox/circuits/models/providers.py +++ b/netbox/circuits/models/providers.py @@ -63,7 +63,7 @@ class ProviderAccount(PrimaryModel): ) account = models.CharField( max_length=100, - verbose_name=_('Account ID') + verbose_name=_('account ID') ) name = models.CharField( verbose_name=_('name'), @@ -118,7 +118,7 @@ class ProviderNetwork(PrimaryModel): service_id = models.CharField( max_length=100, blank=True, - verbose_name=_('Service ID') + verbose_name=_('service ID') ) class Meta: diff --git a/netbox/circuits/tables/circuits.py b/netbox/circuits/tables/circuits.py index 3ad01e87a..7b1accdc3 100644 --- a/netbox/circuits/tables/circuits.py +++ b/netbox/circuits/tables/circuits.py @@ -1,4 +1,3 @@ -from django.utils.translation import gettext_lazy as _ import django_tables2 as tables from circuits.models import * @@ -54,19 +53,19 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): ) provider_account = tables.Column( linkify=True, - verbose_name=_('Account') + verbose_name='Account' ) status = columns.ChoiceFieldColumn() termination_a = tables.TemplateColumn( template_code=CIRCUITTERMINATION_LINK, - verbose_name=_('Side A') + verbose_name='Side A' ) termination_z = tables.TemplateColumn( template_code=CIRCUITTERMINATION_LINK, - verbose_name=_('Side Z') + verbose_name='Side Z' ) commit_rate = CommitRateColumn( - verbose_name=_('Commit Rate') + verbose_name='Commit Rate' ) comments = columns.MarkdownColumn() tags = columns.TagColumn( diff --git a/netbox/core/models/data.py b/netbox/core/models/data.py index 6ee0324c8..d1a033798 100644 --- a/netbox/core/models/data.py +++ b/netbox/core/models/data.py @@ -266,7 +266,8 @@ class DataFile(models.Model): help_text=_("File path relative to the data source's root") ) size = models.PositiveIntegerField( - editable=False + editable=False, + verbose_name=_('size') ) hash = models.CharField( verbose_name=_('hash'), diff --git a/netbox/core/models/jobs.py b/netbox/core/models/jobs.py index eda9f7bd0..63a918eb5 100644 --- a/netbox/core/models/jobs.py +++ b/netbox/core/models/jobs.py @@ -93,7 +93,7 @@ class Job(models.Model): blank=True ) job_id = models.UUIDField( - verbose_name=_('job id'), + verbose_name=_('job ID'), unique=True ) diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 8325d0acc..5212dbff3 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -242,7 +242,7 @@ class CableTermination(ChangeLoggedModel): cable_end = models.CharField( max_length=1, choices=CableEndChoices, - verbose_name=_('End') + verbose_name=_('end') ) termination_type = models.ForeignKey( to=ContentType, diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index 113044129..94ff2a8ce 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -43,9 +43,9 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin): name = models.CharField( verbose_name=_('name'), max_length=64, - help_text=_(""" - {module} is accepted as a substitution for the module bay position when attached to a module type. - """) + help_text=_( + "{module} is accepted as a substitution for the module bay position when attached to a module type." + ) ) _name = NaturalOrderingField( target_field='name', @@ -59,6 +59,7 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin): help_text=_('Physical label') ) description = models.CharField( + verbose_name=_('description'), max_length=200, blank=True ) @@ -378,7 +379,7 @@ class InterfaceTemplate(ModularComponentTemplateModel): ) mgmt_only = models.BooleanField( default=False, - verbose_name=_('Management only') + verbose_name=_('management only') ) bridge = models.ForeignKey( to='self', @@ -386,7 +387,7 @@ class InterfaceTemplate(ModularComponentTemplateModel): related_name='bridge_interfaces', null=True, blank=True, - verbose_name=_('Bridge interface') + verbose_name=_('bridge interface') ) poe_mode = models.CharField( max_length=50, @@ -404,7 +405,7 @@ class InterfaceTemplate(ModularComponentTemplateModel): max_length=30, choices=WirelessRoleChoices, blank=True, - verbose_name='Wireless role' + verbose_name=_('wireless role') ) component_model = Interface @@ -703,7 +704,7 @@ class InventoryItemTemplate(MPTTModel, ComponentTemplateModel): ) part_id = models.CharField( max_length=50, - verbose_name=_('Part ID'), + verbose_name=_('part ID'), blank=True, help_text=_('Manufacturer-assigned part identifier') ) diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 543528207..b9fe451e9 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -200,7 +200,9 @@ class CabledObjectModel(models.Model): @property def parent_object(self): - raise NotImplementedError(_("{class_name} models must declare a parent_object property").format(class_name=self.__class__.__name__)) + raise NotImplementedError( + _("{class_name} models must declare a parent_object property").format(class_name=self.__class__.__name__) + ) @property def opposite_cable_end(self): @@ -366,7 +368,9 @@ class PowerPort(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracking if self.maximum_draw is not None and self.allocated_draw is not None: if self.allocated_draw > self.maximum_draw: raise ValidationError({ - 'allocated_draw': _("Allocated draw cannot exceed the maximum draw ({maximum_draw}W).").format(maximum_draw=self.maximum_draw) + 'allocated_draw': _( + "Allocated draw cannot exceed the maximum draw ({maximum_draw}W)." + ).format(maximum_draw=self.maximum_draw) }) def get_downstream_powerports(self, leg=None): @@ -477,7 +481,9 @@ class PowerOutlet(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracki # Validate power port assignment if self.power_port and self.power_port.device != self.device: - raise ValidationError(_("Parent power port ({power_port}) must belong to the same device").format(power_port=self.power_port)) + raise ValidationError( + _("Parent power port ({power_port}) must belong to the same device").format(power_port=self.power_port) + ) # @@ -495,7 +501,7 @@ class BaseInterface(models.Model): mac_address = MACAddressField( null=True, blank=True, - verbose_name=_('MAC Address') + verbose_name=_('MAC address') ) mtu = models.PositiveIntegerField( blank=True, @@ -575,7 +581,7 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd related_name='member_interfaces', null=True, blank=True, - verbose_name=_('Parent LAG') + verbose_name=_('parent LAG') ) type = models.CharField( verbose_name=_('type'), @@ -584,13 +590,13 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd ) mgmt_only = models.BooleanField( default=False, - verbose_name=_('Management only'), + verbose_name=_('management only'), help_text=_('This interface is used only for out-of-band management') ) speed = models.PositiveIntegerField( blank=True, null=True, - verbose_name=_('Speed (Kbps)') + verbose_name=_('speed (Kbps)') ) duplex = models.CharField( verbose_name=_('duplex'), @@ -609,20 +615,20 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd max_length=30, choices=WirelessRoleChoices, blank=True, - verbose_name=_('Wireless role') + verbose_name=_('wireless role') ) rf_channel = models.CharField( max_length=50, choices=WirelessChannelChoices, blank=True, - verbose_name=_('Wireless channel') + verbose_name=_('wireless channel') ) rf_channel_frequency = models.DecimalField( max_digits=7, decimal_places=2, blank=True, null=True, - verbose_name=_('Channel frequency (MHz)'), + verbose_name=_('channel frequency (MHz)'), help_text=_("Populated by selected channel (if set)") ) rf_channel_width = models.DecimalField( @@ -630,14 +636,14 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd decimal_places=3, blank=True, null=True, - verbose_name=('Channel width (MHz)'), + verbose_name=('channel width (MHz)'), help_text=_("Populated by selected channel (if set)") ) tx_power = models.PositiveSmallIntegerField( blank=True, null=True, validators=(MaxValueValidator(127),), - verbose_name=_('Transmit power (dBm)') + verbose_name=_('transmit power (dBm)') ) poe_mode = models.CharField( max_length=50, @@ -662,7 +668,7 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd to='wireless.WirelessLAN', related_name='interfaces', blank=True, - verbose_name=_('Wireless LANs') + verbose_name=_('wireless LANs') ) untagged_vlan = models.ForeignKey( to='ipam.VLAN', @@ -670,13 +676,13 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd related_name='interfaces_as_untagged', null=True, blank=True, - verbose_name=_('Untagged VLAN') + verbose_name=_('untagged VLAN') ) tagged_vlans = models.ManyToManyField( to='ipam.VLAN', related_name='interfaces_as_tagged', blank=True, - verbose_name=_('Tagged VLANs') + verbose_name=_('tagged VLANs') ) vrf = models.ForeignKey( to='ipam.VRF', @@ -722,13 +728,17 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd # Virtual Interfaces cannot have a Cable attached if self.is_virtual and self.cable: raise ValidationError({ - 'type': _("{display_type} interfaces cannot have a cable attached.").format(display_type=self.get_type_display()) + 'type': _("{display_type} interfaces cannot have a cable attached.").format( + display_type=self.get_type_display() + ) }) # Virtual Interfaces cannot be marked as connected if self.is_virtual and self.mark_connected: raise ValidationError({ - 'mark_connected': _("{display_type} interfaces cannot be marked as connected.".format(display_type=self.get_type_display())) + 'mark_connected': _("{display_type} interfaces cannot be marked as connected.".format( + display_type=self.get_type_display()) + ) }) # Parent validation @@ -745,15 +755,20 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd if self.parent and self.parent.device != self.device: if self.device.virtual_chassis is None: raise ValidationError({ - 'parent': _("The selected parent interface ({selected_parent}) belongs to a different device ({parent_device})").format( - selected_parent=self.parent, parent_device=self.parent.device) + 'parent': _( + "The selected parent interface ({interface}) belongs to a different device ({device})" + ).format(interface=self.parent, device=self.parent.device) }) elif self.parent.device.virtual_chassis != self.parent.virtual_chassis: raise ValidationError({ - 'parent': _(""" - The selected parent interface ({parent}) belongs to {parent_device}, which - is not part of virtual chassis {virtual_chassis}. - """).format(parent=self.parent, parent_device=self.parent_device, virtual_chassis=self.device.virtual_chassis) + 'parent': _( + "The selected parent interface ({interface}) belongs to {device}, which is not part of " + "virtual chassis {virtual_chassis}." + ).format( + interface=self.parent, + device=self.parent_device, + virtual_chassis=self.device.virtual_chassis + ) }) # Bridge validation @@ -772,10 +787,12 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd }) elif self.bridge.device.virtual_chassis != self.device.virtual_chassis: raise ValidationError({ - 'bridge': _(""" - The selected bridge interface ({bridge}) belongs to {device}, which " - is not part of virtual chassis {virtual_chassis}. - """).format(bridge=self.bridge, device=self.bridge.device, virtual_chassis=self.device.virtual_chassis) + 'bridge': _( + "The selected bridge interface ({interface}) belongs to {device}, which is not part of virtual " + "chassis {virtual_chassis}." + ).format( + interface=self.bridge, device=self.bridge.device, virtual_chassis=self.device.virtual_chassis + ) }) # LAG validation @@ -792,14 +809,17 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd if self.lag and self.lag.device != self.device: if self.device.virtual_chassis is None: raise ValidationError({ - 'lag': _("The selected LAG interface ({lag}) belongs to a different device ({device}).").format(lag=self.lag, device=self.lag.device) + 'lag': _( + "The selected LAG interface ({lag}) belongs to a different device ({device})." + ).format(lag=self.lag, device=self.lag.device) }) elif self.lag.device.virtual_chassis != self.device.virtual_chassis: raise ValidationError({ - 'lag': _(""" - The selected LAG interface ({lag}) belongs to {device}, which is not part - of virtual chassis {virtual_chassis}. - """.format(lag=self.lag, device=self.lag.device, virtual_chassis=self.device.virtual_chassis)) + 'lag': _( + "The selected LAG interface ({lag}) belongs to {device}, which is not part of virtual chassis " + "{virtual_chassis}.".format( + lag=self.lag, device=self.lag.device, virtual_chassis=self.device.virtual_chassis) + ) }) # PoE validation @@ -935,7 +955,7 @@ class FrontPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin): related_name='frontports' ) rear_port_position = models.PositiveSmallIntegerField( - verbose_name=_('rear_port_position'), + verbose_name=_('rear port position'), default=1, validators=[ MinValueValidator(REARPORT_POSITIONS_MIN), @@ -969,16 +989,22 @@ class FrontPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin): # Validate rear port assignment if self.rear_port.device != self.device: raise ValidationError({ - "rear_port": _("Rear port ({rear_port}) must belong to the same device").format(rear_port=self.rear_port) + "rear_port": _( + "Rear port ({rear_port}) must belong to the same device" + ).format(rear_port=self.rear_port) }) # Validate rear port position assignment if self.rear_port_position > self.rear_port.positions: raise ValidationError({ - "rear_port_position": _(""" - Invalid rear port position ({rear_port_position}): Rear port - {name} has only {positions} positions - """).format(rear_port_position=self.rear_port_position, name=self.rear_port.name, positions=self.rear_port.positions) + "rear_port_position": _( + "Invalid rear port position ({rear_port_position}): Rear port {name} has only {positions} " + "positions." + ).format( + rear_port_position=self.rear_port_position, + name=self.rear_port.name, + positions=self.rear_port.positions + ) }) @@ -1066,8 +1092,8 @@ class DeviceBay(ComponentModel, TrackingModelMixin): # Validate that the parent Device can have DeviceBays if not self.device.device_type.is_parent_device: - raise ValidationError(_("This type of device ({}) does not support device bays.").format( - self.device.device_type + raise ValidationError(_("This type of device ({device_type}) does not support device bays.").format( + device_type=self.device.device_type )) # Cannot install a device into itself, obviously @@ -1079,9 +1105,9 @@ class DeviceBay(ComponentModel, TrackingModelMixin): current_bay = DeviceBay.objects.filter(installed_device=self.installed_device).first() if current_bay and current_bay != self: raise ValidationError({ - 'installed_device': _("Cannot install the specified device; device is already installed in {}").format( - current_bay - ) + 'installed_device': _( + "Cannot install the specified device; device is already installed in {bay}." + ).format(bay=current_bay) }) @@ -1148,13 +1174,13 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin): ) part_id = models.CharField( max_length=50, - verbose_name=_('Part ID'), + verbose_name=_('part ID'), blank=True, help_text=_('Manufacturer-assigned part identifier') ) serial = models.CharField( max_length=50, - verbose_name=_('Serial number'), + verbose_name=_('serial number'), blank=True ) asset_tag = models.CharField( @@ -1162,7 +1188,7 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin): unique=True, blank=True, null=True, - verbose_name=_('Asset tag'), + verbose_name=_('asset tag'), help_text=_('A unique tag used to identify this item') ) discovered = models.BooleanField( diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index c21718244..bb7a8d573 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -91,7 +91,7 @@ class DeviceType(PrimaryModel, WeightMixin): related_name='+', blank=True, null=True, - verbose_name=_('Default platform') + verbose_name=_('default platform') ) part_number = models.CharField( verbose_name=_('part number'), @@ -103,18 +103,18 @@ class DeviceType(PrimaryModel, WeightMixin): max_digits=4, decimal_places=1, default=1.0, - verbose_name=_('Height (U)') + verbose_name=_('height (U)') ) is_full_depth = models.BooleanField( default=True, - verbose_name=_('Is full depth'), + verbose_name=_('is full depth'), help_text=_('Device consumes both front and rear rack faces') ) subdevice_role = models.CharField( max_length=50, choices=SubdeviceRoleChoices, blank=True, - verbose_name=_('Parent/child status'), + verbose_name=_('parent/child status'), help_text=_('Parent devices house child devices in device bays. Leave blank ' 'if this device type is neither a parent nor a child.') ) @@ -180,7 +180,8 @@ class DeviceType(PrimaryModel, WeightMixin): ) clone_fields = ( - 'manufacturer', 'default_platform', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit' + 'manufacturer', 'default_platform', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', + 'weight_unit', ) prerequisite_models = ( 'dcim.Manufacturer', @@ -310,10 +311,10 @@ class DeviceType(PrimaryModel, WeightMixin): if racked_instance_count: url = f"{reverse('dcim:device_list')}?manufactuer_id={self.manufacturer_id}&device_type_id={self.pk}" raise ValidationError({ - 'u_height': mark_safe( - _('Unable to set 0U height: Found {racked_instance_count} instances already ' - 'mounted within racks.').format(url=url, racked_instance_count=racked_instance_count) - ) + 'u_height': mark_safe(_( + 'Unable to set 0U height: Found {racked_instance_count} instances already ' + 'mounted within racks.' + ).format(url=url, racked_instance_count=racked_instance_count)) }) if ( @@ -465,7 +466,7 @@ class DeviceRole(OrganizationalModel): ) vm_role = models.BooleanField( default=True, - verbose_name=_('VM Role'), + verbose_name=_('VM role'), help_text=_('Virtual machines may be assigned to this role') ) config_template = models.ForeignKey( @@ -571,7 +572,7 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): serial = models.CharField( max_length=50, blank=True, - verbose_name=_('Serial number'), + verbose_name=_('serial number'), help_text=_("Chassis serial number, assigned by the manufacturer") ) asset_tag = models.CharField( @@ -579,7 +580,7 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): blank=True, null=True, unique=True, - verbose_name=_('Asset tag'), + verbose_name=_('asset tag'), help_text=_('A unique tag used to identify this device') ) site = models.ForeignKey( @@ -607,14 +608,14 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): blank=True, null=True, validators=[MinValueValidator(1), MaxValueValidator(RACK_U_HEIGHT_MAX + 0.5)], - verbose_name=_('Position (U)'), + verbose_name=_('position (U)'), help_text=_('The lowest-numbered unit occupied by the device') ) face = models.CharField( max_length=50, blank=True, choices=DeviceFaceChoices, - verbose_name=_('Rack face') + verbose_name=_('rack face') ) status = models.CharField( verbose_name=_('status'), @@ -634,7 +635,7 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): related_name='+', blank=True, null=True, - verbose_name=_('Primary IPv4') + verbose_name=_('primary IPv4') ) primary_ip6 = models.OneToOneField( to='ipam.IPAddress', @@ -642,7 +643,7 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): related_name='+', blank=True, null=True, - verbose_name=_('Primary IPv6') + verbose_name=_('primary IPv6') ) oob_ip = models.OneToOneField( to='ipam.IPAddress', @@ -650,7 +651,7 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): related_name='+', blank=True, null=True, - verbose_name='Out-of-band IP' + verbose_name=_('out-of-band IP') ) cluster = models.ForeignKey( to='virtualization.Cluster', @@ -667,14 +668,14 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): null=True ) vc_position = models.PositiveSmallIntegerField( - verbose_name=_('vc position'), + verbose_name=_('VC position'), blank=True, null=True, validators=[MaxValueValidator(255)], help_text=_('Virtual chassis position') ) vc_priority = models.PositiveSmallIntegerField( - verbose_name=_('vc priority'), + verbose_name=_('VC priority'), blank=True, null=True, validators=[MaxValueValidator(255)], @@ -817,11 +818,15 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): }) if self.location and self.site != self.location.site: raise ValidationError({ - 'location': _("Location {location} does not belong to site {site}.").format(location=self.location, site=self.site), + 'location': _( + "Location {location} does not belong to site {site}." + ).format(location=self.location, site=self.site) }) if self.rack and self.location and self.rack.location != self.location: raise ValidationError({ - 'rack': _("Rack {rack} does not belong to location {location}.").format(rack=self.rack, location=self.location), + 'rack': _( + "Rack {rack} does not belong to location {location}." + ).format(rack=self.rack, location=self.location) }) if self.rack is None: @@ -848,7 +853,9 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): if hasattr(self, 'device_type'): if self.position and self.device_type.u_height == 0: raise ValidationError({ - 'position': _("A U0 device type ({device_type}) cannot be assigned to a rack position.").format(device_type=self.device_type) + 'position': _( + "A U0 device type ({device_type}) cannot be assigned to a rack position." + ).format(device_type=self.device_type) }) if self.rack: @@ -857,13 +864,17 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): # Child devices cannot be assigned to a rack face/unit if self.device_type.is_child_device and self.face: raise ValidationError({ - 'face': _("Child device types cannot be assigned to a rack face. This is an attribute of the " - "parent device.") + 'face': _( + "Child device types cannot be assigned to a rack face. This is an attribute of the parent " + "device." + ) }) if self.device_type.is_child_device and self.position: raise ValidationError({ - 'position': _("Child device types cannot be assigned to a rack position. This is an attribute of " - "the parent device.") + 'position': _( + "Child device types cannot be assigned to a rack position. This is an attribute of the " + "parent device." + ) }) # Validate rack space @@ -874,9 +885,12 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): ) if self.position and self.position not in available_units: raise ValidationError({ - 'position': _("U{position} is already occupied or does not have sufficient space to " - "accommodate this device type: {device_type} ({u_height}U)").format( - position=self.position, device_type=self.device_type, u_height=self.device_type.u_height) + 'position': _( + "U{position} is already occupied or does not have sufficient space to accommodate this " + "device type: {device_type} ({u_height}U)" + ).format( + position=self.position, device_type=self.device_type, u_height=self.device_type.u_height + ) }) except DeviceType.DoesNotExist: @@ -895,7 +909,9 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): pass else: raise ValidationError({ - 'primary_ip4': _("The specified IP address ({primary_ip4}) is not assigned to this device.").format(primary_ip4=self.primary_ip4) + 'primary_ip4': _( + "The specified IP address ({primary_ip4}) is not assigned to this device." + ).format(primary_ip4=self.primary_ip4) }) if self.primary_ip6: if self.primary_ip6.family != 6: @@ -908,7 +924,9 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): pass else: raise ValidationError({ - 'primary_ip6': _("The specified IP address ({primary_ip6}) is not assigned to this device.").format(primary_ip6=self.primary_ip6) + 'primary_ip6': _( + "The specified IP address ({primary_ip6}) is not assigned to this device." + ).format(primary_ip6=self.primary_ip6) }) if self.oob_ip: if self.oob_ip.assigned_object in vc_interfaces: @@ -924,9 +942,13 @@ class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin): if hasattr(self, 'device_type') and self.platform: if self.platform.manufacturer and self.platform.manufacturer != self.device_type.manufacturer: raise ValidationError({ - 'platform': _("The assigned platform is limited to {platform_manufacturer} device types, but " - "this device's type belongs to {device_type_manufacturer}.").format( - platform_manufacturer=self.platform.manufacturer, device_type_manufacturer=self.device_type.manufacturer) + 'platform': _( + "The assigned platform is limited to {platform_manufacturer} device types, but this device's " + "type belongs to {device_type_manufacturer}." + ).format( + platform_manufacturer=self.platform.manufacturer, + device_type_manufacturer=self.device_type.manufacturer + ) }) # A Device can only be assigned to a Cluster in the same Site (or no Site) @@ -1131,14 +1153,14 @@ class Module(PrimaryModel, ConfigContextModel): serial = models.CharField( max_length=50, blank=True, - verbose_name=_('Serial number') + verbose_name=_('serial number') ) asset_tag = models.CharField( max_length=50, blank=True, null=True, unique=True, - verbose_name=_('Asset tag'), + verbose_name=_('asset tag'), help_text=_('A unique tag used to identify this device') ) @@ -1161,7 +1183,9 @@ class Module(PrimaryModel, ConfigContextModel): if hasattr(self, "module_bay") and (self.module_bay.device != self.device): raise ValidationError( - _("Module must be installed within a module bay belonging to the assigned device ({device}).").format(device=self.device) + _("Module must be installed within a module bay belonging to the assigned device ({device}).").format( + device=self.device + ) ) def save(self, *args, **kwargs): @@ -1291,7 +1315,9 @@ class VirtualChassis(PrimaryModel): # VirtualChassis.) if self.pk and self.master and self.master not in self.members.all(): raise ValidationError({ - 'master': _("The selected master ({master}) is not assigned to this virtual chassis.").format(master=self.master) + 'master': _("The selected master ({master}) is not assigned to this virtual chassis.").format( + master=self.master + ) }) def delete(self, *args, **kwargs): @@ -1304,10 +1330,10 @@ class VirtualChassis(PrimaryModel): lag__device=F('device') ) if interfaces: - raise ProtectedError( - _("Unable to delete virtual chassis {self}. There are member interfaces which form a cross-chassis LAG interfaces").format( - self=self, interfaces=InterfaceSpeedChoices), - ) + raise ProtectedError(_( + "Unable to delete virtual chassis {self}. There are member interfaces which form a cross-chassis LAG " + "interfaces." + ).format(self=self, interfaces=InterfaceSpeedChoices)) return super().delete(*args, **kwargs) @@ -1341,7 +1367,7 @@ class VirtualDeviceContext(PrimaryModel): related_name='+', blank=True, null=True, - verbose_name=_('Primary IPv4') + verbose_name=_('primary IPv4') ) primary_ip6 = models.OneToOneField( to='ipam.IPAddress', @@ -1349,7 +1375,7 @@ class VirtualDeviceContext(PrimaryModel): related_name='+', blank=True, null=True, - verbose_name=_('Primary IPv6') + verbose_name=_('primary IPv6') ) tenant = models.ForeignKey( to='tenancy.Tenant', @@ -1359,7 +1385,7 @@ class VirtualDeviceContext(PrimaryModel): null=True ) comments = models.TextField( - verbose_name=_('comment'), + verbose_name=_('comments'), blank=True ) @@ -1405,7 +1431,9 @@ class VirtualDeviceContext(PrimaryModel): continue if primary_ip.family != family: raise ValidationError({ - f'primary_ip{family}': _("{primary_ip} is not an IPv{family} address.").format(family=family, primary_ip=primary_ip) + f'primary_ip{family}': _( + "{primary_ip} is not an IPv{family} address." + ).format(family=family, primary_ip=primary_ip) }) device_interfaces = self.device.vc_interfaces(if_master=False) if primary_ip.assigned_object not in device_interfaces: diff --git a/netbox/dcim/models/mixins.py b/netbox/dcim/models/mixins.py index 882b3d96b..f787c8e97 100644 --- a/netbox/dcim/models/mixins.py +++ b/netbox/dcim/models/mixins.py @@ -14,7 +14,7 @@ class WeightMixin(models.Model): null=True ) weight_unit = models.CharField( - verbose_name=_('weight_unit'), + verbose_name=_('weight unit'), max_length=50, choices=WeightUnitChoices, blank=True, diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index 2014e7a35..13fb41b59 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -65,7 +65,7 @@ class Rack(PrimaryModel, WeightMixin): max_length=50, blank=True, null=True, - verbose_name=_('Facility ID'), + verbose_name=_('facility ID'), help_text=_("Locally-assigned identifier") ) site = models.ForeignKey( @@ -104,42 +104,42 @@ class Rack(PrimaryModel, WeightMixin): serial = models.CharField( max_length=50, blank=True, - verbose_name=_('Serial number') + verbose_name=_('serial number') ) asset_tag = models.CharField( max_length=50, blank=True, null=True, unique=True, - verbose_name=_('Asset tag'), + verbose_name=_('asset tag'), help_text=_('A unique tag used to identify this rack') ) type = models.CharField( choices=RackTypeChoices, max_length=50, blank=True, - verbose_name=_('Type') + verbose_name=_('type') ) width = models.PositiveSmallIntegerField( choices=RackWidthChoices, default=RackWidthChoices.WIDTH_19IN, - verbose_name=_('Width'), + verbose_name=_('width'), help_text=_('Rail-to-rail width') ) u_height = models.PositiveSmallIntegerField( default=RACK_U_HEIGHT_DEFAULT, - verbose_name=_('Height (U)'), + verbose_name=_('height (U)'), validators=[MinValueValidator(1), MaxValueValidator(RACK_U_HEIGHT_MAX)], help_text=_('Height in rack units') ) starting_unit = models.PositiveSmallIntegerField( default=RACK_STARTING_UNIT_DEFAULT, - verbose_name=_('Starting unit'), + verbose_name=_('starting unit'), help_text=_('Starting unit for rack') ) desc_units = models.BooleanField( default=False, - verbose_name=_('Descending units'), + verbose_name=_('descending units'), help_text=_('Units are numbered top-to-bottom') ) outer_width = models.PositiveSmallIntegerField( diff --git a/netbox/dcim/models/sites.py b/netbox/dcim/models/sites.py index 5a9945e8b..30d96e67d 100644 --- a/netbox/dcim/models/sites.py +++ b/netbox/dcim/models/sites.py @@ -181,6 +181,7 @@ class Site(PrimaryModel): null=True ) facility = models.CharField( + verbose_name=_('facility'), max_length=50, blank=True, help_text=_('Local facility ID or description') @@ -337,4 +338,6 @@ class Location(NestedGroupModel): # Parent Location (if any) must belong to the same Site if self.parent and self.parent.site != self.site: - raise ValidationError(_("Parent location ({parent}) must belong to the same site ({site})").format(parent=self.parent, site=self.site)) + raise ValidationError(_( + "Parent location ({parent}) must belong to the same site ({site})." + ).format(parent=self.parent, site=self.site)) diff --git a/netbox/extras/models/change_logging.py b/netbox/extras/models/change_logging.py index 818629fd7..8725fa1aa 100644 --- a/netbox/extras/models/change_logging.py +++ b/netbox/extras/models/change_logging.py @@ -77,13 +77,13 @@ class ObjectChange(models.Model): editable=False ) prechange_data = models.JSONField( - verbose_name=_('prechange data'), + verbose_name=_('pre-change data'), editable=False, blank=True, null=True ) postchange_data = models.JSONField( - verbose_name=_('postchange data'), + verbose_name=_('post-change data'), editable=False, blank=True, null=True diff --git a/netbox/extras/models/configs.py b/netbox/extras/models/configs.py index ce6ed3de2..3ff51f393 100644 --- a/netbox/extras/models/configs.py +++ b/netbox/extras/models/configs.py @@ -221,7 +221,7 @@ class ConfigTemplate(SyncedDataMixin, ExportTemplatesMixin, TagsMixin, ChangeLog help_text=_('Jinja2 template code.') ) environment_params = models.JSONField( - verbose_name=_('environment params'), + verbose_name=_('environment parameters'), blank=True, null=True, default=dict, diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 8bb4167ec..35c001716 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -100,8 +100,9 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): verbose_name=_('label'), max_length=50, blank=True, - help_text=_('Name of the field as displayed to users (if not provided, ' - 'the field\'s name will be used)') + help_text=_( + "Name of the field as displayed to users (if not provided, 'the field's name will be used)" + ) ) group_name = models.CharField( verbose_name=_('group name'), @@ -117,52 +118,53 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): required = models.BooleanField( verbose_name=_('required'), default=False, - help_text=_('If true, this field is required when creating new objects ' - 'or editing an existing object.') + help_text=_("If true, this field is required when creating new objects or editing an existing object.") ) search_weight = models.PositiveSmallIntegerField( verbose_name=_('search weight'), default=1000, - help_text=_('Weighting for search. Lower values are considered more important. ' - 'Fields with a search weight of zero will be ignored.') + help_text=_( + "Weighting for search. Lower values are considered more important. Fields with a search weight of zero " + "will be ignored." + ) ) filter_logic = models.CharField( verbose_name=_('filter logic'), max_length=50, choices=CustomFieldFilterLogicChoices, default=CustomFieldFilterLogicChoices.FILTER_LOOSE, - help_text=_('Loose matches any instance of a given string; exact ' - 'matches the entire field.') + help_text=_("Loose matches any instance of a given string; exact matches the entire field.") ) default = models.JSONField( verbose_name=_('default'), blank=True, null=True, - help_text=_('Default value for the field (must be a JSON value). Encapsulate ' - 'strings with double quotes (e.g. "Foo").') + help_text=_( + 'Default value for the field (must be a JSON value). Encapsulate strings with double quotes (e.g. "Foo").' + ) ) weight = models.PositiveSmallIntegerField( default=100, - verbose_name=_('Display weight'), + verbose_name=_('display weight'), help_text=_('Fields with higher weights appear lower in a form.') ) validation_minimum = models.IntegerField( blank=True, null=True, - verbose_name=_('Minimum value'), + verbose_name=_('minimum value'), help_text=_('Minimum allowed value (for numeric fields)') ) validation_maximum = models.IntegerField( blank=True, null=True, - verbose_name=_('Maximum value'), + verbose_name=_('maximum value'), help_text=_('Maximum allowed value (for numeric fields)') ) validation_regex = models.CharField( blank=True, validators=[validate_regex], max_length=500, - verbose_name=_('Validation regex'), + verbose_name=_('validation regex'), help_text=_( 'Regular expression to enforce on text field values. Use ^ and $ to force matching of entire string. For ' 'example, ^[A-Z]{3}$ will limit values to exactly three uppercase letters.' @@ -185,7 +187,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): ) is_cloneable = models.BooleanField( default=False, - verbose_name=_('Cloneable'), + verbose_name=_('is cloneable'), help_text=_('Replicate this value when cloning objects') ) @@ -275,7 +277,9 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): self.validate(default_value) except ValidationError as err: raise ValidationError({ - 'default': _('Invalid default value "{default}": {message}').format(default=self.default, message=self.message) + 'default': _( + 'Invalid default value "{default}": {message}' + ).format(default=self.default, message=self.message) }) # Minimum/maximum values can be set only for numeric fields @@ -313,7 +317,9 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): # A selection field's default (if any) must be present in its available choices if self.type == CustomFieldTypeChoices.TYPE_SELECT and self.default and self.default not in self.choices: raise ValidationError({ - 'default': _("The specified default value ({default}) is not listed as an available choice.").format(default=self.default) + 'default': _( + "The specified default value ({default}) is not listed as an available choice." + ).format(default=self.default) }) # Object fields must define an object_type; other fields must not @@ -324,7 +330,9 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): }) elif self.object_type: raise ValidationError({ - 'object_type': _("{type_display} fields may not define an object type.".format(type_display=self.get_type_display())) + 'object_type': _( + "{type_display} fields may not define an object type.") + .format(type_display=self.get_type_display()) }) def serialize(self, value): @@ -473,7 +481,9 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): field.validators = [ RegexValidator( regex=self.validation_regex, - message=mark_safe(_("Values must match this regex: {regex}").format(regex=self.validation_regex)) + message=mark_safe(_("Values must match this regex: {regex}").format( + regex=self.validation_regex + )) ) ] @@ -577,9 +587,13 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): if type(value) is not int: raise ValidationError(_("Value must be an integer.")) if self.validation_minimum is not None and value < self.validation_minimum: - raise ValidationError(_("Value must be at least {validation_minimum}").format(validation_minimum=self.validation_maximum)) + raise ValidationError( + _("Value must be at least {minimum}").format(minimum=self.validation_maximum) + ) if self.validation_maximum is not None and value > self.validation_maximum: - raise ValidationError(_("Value must not exceed {validation_maximum}").format(validation_maximum=self.validation_maximum)) + raise ValidationError( + _("Value must not exceed {maximum}").format(maximum=self.validation_maximum) + ) # Validate decimal elif self.type == CustomFieldTypeChoices.TYPE_DECIMAL: @@ -588,9 +602,13 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): except decimal.InvalidOperation: raise ValidationError(_("Value must be a decimal.")) if self.validation_minimum is not None and value < self.validation_minimum: - raise ValidationError(_("Value must be at least {validation_minimum}").format(validation_minimum=self.validation_minimum)) + raise ValidationError( + _("Value must be at least {minimum}").format(minimum=self.validation_minimum) + ) if self.validation_maximum is not None and value > self.validation_maximum: - raise ValidationError(_("Value must not exceed {validation_maximum}").format(validation_maximum=self.validation_maximum)) + raise ValidationError( + _("Value must not exceed {maximum}").format(maximum=self.validation_maximum) + ) # Validate boolean elif self.type == CustomFieldTypeChoices.TYPE_BOOLEAN and value not in [True, False, 1, 0]: @@ -610,13 +628,17 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): try: datetime.fromisoformat(value) except ValueError: - raise ValidationError(_("Date and time values must be in ISO 8601 format (YYYY-MM-DD HH:MM:SS).")) + raise ValidationError( + _("Date and time values must be in ISO 8601 format (YYYY-MM-DD HH:MM:SS).") + ) # Validate selected choice elif self.type == CustomFieldTypeChoices.TYPE_SELECT: if value not in self.choices: raise ValidationError( - _("Invalid choice ({value}). Available choices are: {choices}").format(value=value, choices=', '.join(self.choices)) + _("Invalid choice ({value}). Available choices are: {choices}").format( + value=value, choices=', '.join(self.choices) + ) ) # Validate all selected choices @@ -635,7 +657,9 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): # Validate selected objects elif self.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT: if type(value) is not list: - raise ValidationError(_("Value must be a list of object IDs, not {type}").format(type=type(value).__name__)) + raise ValidationError( + _("Value must be a list of object IDs, not {type}").format(type=type(value).__name__) + ) for id in value: if type(id) is not int: raise ValidationError(_("Found invalid object ID: {id}").format(id=id)) diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index 33837a027..b209f0d5b 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -48,7 +48,7 @@ class Webhook(ExportTemplatesMixin, ChangeLoggedModel): content_types = models.ManyToManyField( to=ContentType, related_name='webhooks', - verbose_name='Object types', + verbose_name=_('object types'), limit_choices_to=FeatureQuery('webhooks'), help_text=_("The object(s) to which this Webhook applies.") ) @@ -58,35 +58,37 @@ class Webhook(ExportTemplatesMixin, ChangeLoggedModel): unique=True ) type_create = models.BooleanField( - verbose_name=_('type create'), + verbose_name=_('on create'), default=False, help_text=_("Triggers when a matching object is created.") ) type_update = models.BooleanField( - verbose_name=_('type update'), + verbose_name=_('on update'), default=False, help_text=_("Triggers when a matching object is updated.") ) type_delete = models.BooleanField( - verbose_name=_('type delete'), + verbose_name=_('on delete'), default=False, help_text=_("Triggers when a matching object is deleted.") ) type_job_start = models.BooleanField( - verbose_name=_('type job start'), + verbose_name=_('on job start'), default=False, help_text=_("Triggers when a job for a matching object is started.") ) type_job_end = models.BooleanField( - verbose_name=_('type job end'), + verbose_name=_('on job end'), default=False, help_text=_("Triggers when a job for a matching object terminates.") ) payload_url = models.CharField( max_length=500, verbose_name=_('URL'), - help_text=_('This URL will be called using the HTTP method defined when the webhook is called. ' - 'Jinja2 template processing is supported with the same context as the request body.') + help_text=_( + "This URL will be called using the HTTP method defined when the webhook is called. Jinja2 template " + "processing is supported with the same context as the request body." + ) ) enabled = models.BooleanField( verbose_name=_('enabled'), @@ -102,31 +104,37 @@ class Webhook(ExportTemplatesMixin, ChangeLoggedModel): max_length=100, default=HTTP_CONTENT_TYPE_JSON, verbose_name=_('HTTP content type'), - help_text=_('The complete list of official content types is available ' - 'here.') + help_text=_( + 'The complete list of official content types is available ' + 'here.' + ) ) additional_headers = models.TextField( verbose_name=_('additional headers'), blank=True, - help_text=_("User-supplied HTTP headers to be sent with the request in addition to the HTTP content type. " - "Headers should be defined in the format Name: Value. Jinja2 template processing is " - "supported with the same context as the request body (below).") + help_text=_( + "User-supplied HTTP headers to be sent with the request in addition to the HTTP content type. Headers " + "should be defined in the format Name: Value. Jinja2 template processing is supported with " + "the same context as the request body (below)." + ) ) body_template = models.TextField( verbose_name=_('body template'), blank=True, - help_text=_('Jinja2 template for a custom request body. If blank, a JSON object representing the change will be ' - 'included. Available context data includes: event, model, ' - 'timestamp, username, request_id, and data.') + help_text=_( + "Jinja2 template for a custom request body. If blank, a JSON object representing the change will be " + "included. Available context data includes: event, model, " + "timestamp, username, request_id, and data." + ) ) secret = models.CharField( verbose_name=_('secret'), max_length=255, blank=True, - help_text=_("When provided, the request will include a 'X-Hook-Signature' " - "header containing a HMAC hex digest of the payload body using " - "the secret as the key. The secret is not transmitted in " - "the request.") + help_text=_( + "When provided, the request will include a X-Hook-Signature header containing a HMAC hex " + "digest of the payload body using the secret as the key. The secret is not transmitted in the request." + ) ) conditions = models.JSONField( verbose_name=_('conditions'), @@ -144,8 +152,9 @@ class Webhook(ExportTemplatesMixin, ChangeLoggedModel): null=True, blank=True, verbose_name=_('CA File Path'), - help_text=_('The specific CA certificate file to use for SSL verification. ' - 'Leave blank to use the system defaults.') + help_text=_( + "The specific CA certificate file to use for SSL verification. Leave blank to use the system defaults." + ) ) class Meta: @@ -243,7 +252,7 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): help_text=_("Jinja2 template code for link text") ) link_url = models.TextField( - verbose_name=_('Link URL'), + verbose_name=_('link URL'), help_text=_("Jinja2 template code for link URL") ) weight = models.PositiveSmallIntegerField( @@ -333,8 +342,10 @@ class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, Change blank=True ) template_code = models.TextField( - help_text=_('Jinja2 template code. The list of objects being exported is passed as a context variable named ' - 'queryset.') + help_text=_( + "Jinja2 template code. The list of objects being exported is passed as a context variable named " + "queryset." + ) ) mime_type = models.CharField( max_length=50, @@ -626,7 +637,9 @@ class JournalEntry(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ExportTemplat # Prevent the creation of journal entries on unsupported models permitted_types = ContentType.objects.filter(FeatureQuery('journaling').get_query()) if self.assigned_object_type not in permitted_types: - raise ValidationError(_("Journaling is not supported for this object type ({type}).").format(type=self.assigned_object_type)) + raise ValidationError( + _("Journaling is not supported for this object type ({type}).").format(type=self.assigned_object_type) + ) def get_kind_color(self): return JournalEntryKindChoices.colors.get(self.kind) @@ -687,7 +700,7 @@ class ConfigRevision(models.Model): data = models.JSONField( blank=True, null=True, - verbose_name=_('Configuration data') + verbose_name=_('configuration data') ) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/ipam/models/asns.py b/netbox/ipam/models/asns.py index 6e8e89195..57499c676 100644 --- a/netbox/ipam/models/asns.py +++ b/netbox/ipam/models/asns.py @@ -68,7 +68,11 @@ class ASNRange(OrganizationalModel): super().clean() if self.end <= self.start: - raise ValidationError(_("Starting ASN ({start}) must be lower than ending ASN ({end}).").format(start=self.start, end=self.end)) + raise ValidationError( + _("Starting ASN ({start}) must be lower than ending ASN ({end}).").format( + start=self.start, end=self.end + ) + ) def get_child_asns(self): return ASN.objects.filter( diff --git a/netbox/ipam/models/fhrp.py b/netbox/ipam/models/fhrp.py index 4e0f20404..78c34db6a 100644 --- a/netbox/ipam/models/fhrp.py +++ b/netbox/ipam/models/fhrp.py @@ -20,7 +20,7 @@ class FHRPGroup(PrimaryModel): A grouping of next hope resolution protocol (FHRP) peers. (For instance, VRRP or HSRP.) """ group_id = models.PositiveSmallIntegerField( - verbose_name=_('Group ID') + verbose_name=_('group ID') ) name = models.CharField( verbose_name=_('name'), @@ -36,12 +36,12 @@ class FHRPGroup(PrimaryModel): max_length=50, choices=FHRPGroupAuthTypeChoices, blank=True, - verbose_name=_('Authentication type') + verbose_name=_('authentication type') ) auth_key = models.CharField( max_length=255, blank=True, - verbose_name=_('Authentication key') + verbose_name=_('authentication key') ) ip_addresses = GenericRelation( to='ipam.IPAddress', diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index 69eb3b802..66c0a1f4c 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -59,7 +59,7 @@ class RIR(OrganizationalModel): """ is_private = models.BooleanField( default=False, - verbose_name=_('Private'), + verbose_name=_('private'), help_text=_('IP space managed by this RIR is considered private') ) @@ -135,9 +135,9 @@ class Aggregate(GetAvailablePrefixesMixin, PrimaryModel): covering_aggregates = covering_aggregates.exclude(pk=self.pk) if covering_aggregates: raise ValidationError({ - 'prefix': _("Aggregates cannot overlap. {} is already covered by an existing aggregate ({}).").format( - self.prefix, covering_aggregates[0] - ) + 'prefix': _( + "Aggregates cannot overlap. {} is already covered by an existing aggregate ({})." + ).format(self.prefix, covering_aggregates[0]) }) # Ensure that the aggregate being added does not cover an existing aggregate @@ -231,14 +231,13 @@ class Prefix(GetAvailablePrefixesMixin, PrimaryModel): on_delete=models.PROTECT, related_name='prefixes', blank=True, - null=True, - verbose_name='VLAN' + null=True ) status = models.CharField( max_length=50, choices=PrefixStatusChoices, default=PrefixStatusChoices.STATUS_ACTIVE, - verbose_name=_('Status'), + verbose_name=_('status'), help_text=_('Operational status of this prefix') ) role = models.ForeignKey( @@ -250,7 +249,7 @@ class Prefix(GetAvailablePrefixesMixin, PrimaryModel): help_text=_('The primary function of this prefix') ) is_pool = models.BooleanField( - verbose_name=_('Is a pool'), + verbose_name=_('is a pool'), default=False, help_text=_('All IP addresses within this prefix are considered usable') ) @@ -548,23 +547,33 @@ class IPRange(PrimaryModel): # Check that start & end IP versions match if self.start_address.version != self.end_address.version: raise ValidationError({ - 'end_address': _("Ending address version (IPv{end_address_version}) does not match starting " - "address (IPv{start_address_version})").format( - end_address_version=self.end_address.version, start_address_version=self.start_address.version) + 'end_address': _( + "Ending address version (IPv{end_address_version}) does not match starting address " + "(IPv{start_address_version})" + ).format( + end_address_version=self.end_address.version, + start_address_version=self.start_address.version + ) }) # Check that the start & end IP prefix lengths match if self.start_address.prefixlen != self.end_address.prefixlen: raise ValidationError({ - 'end_address': _("Ending address mask (/{end_address_prefixlen}) does not match starting " - "address mask (/{start_address_prefixlen})").format( - end_address_prefixlen=self.end_address.prefixlen, start_address_prefixlen=self.start_address.prefixlen) + 'end_address': _( + "Ending address mask (/{end_address_prefixlen}) does not match starting address mask " + "(/{start_address_prefixlen})" + ).format( + end_address_prefixlen=self.end_address.prefixlen, + start_address_prefixlen=self.start_address.prefixlen + ) }) # Check that the ending address is greater than the starting address if not self.end_address > self.start_address: raise ValidationError({ - 'end_address': _("Ending address must be lower than the starting address ({start_address})").format(start_address=self.start_address) + 'end_address': _( + "Ending address must be lower than the starting address ({start_address})" + ).format(start_address=self.start_address) }) # Check for overlapping ranges @@ -574,13 +583,18 @@ class IPRange(PrimaryModel): Q(start_address__lte=self.start_address, end_address__gte=self.end_address) # Starts & ends outside ).first() if overlapping_range: - raise ValidationError(_("Defined addresses overlap with range {overlapping_range} in VRF {vrf}").format( - overlapping_range=overlapping_range, vrf=self.vrf)) + raise ValidationError( + _("Defined addresses overlap with range {overlapping_range} in VRF {vrf}").format( + overlapping_range=overlapping_range, + vrf=self.vrf + )) # Validate maximum size MAX_SIZE = 2 ** 32 - 1 if int(self.end_address.ip - self.start_address.ip) + 1 > MAX_SIZE: - raise ValidationError(_("Defined range exceeds maximum supported size ({max_size})").format(max_size=MAX_SIZE)) + raise ValidationError( + _("Defined range exceeds maximum supported size ({max_size})").format(max_size=MAX_SIZE) + ) def save(self, *args, **kwargs): @@ -745,14 +759,14 @@ class IPAddress(PrimaryModel): related_name='nat_outside', blank=True, null=True, - verbose_name=_('NAT (Inside)'), + verbose_name=_('NAT (inside)'), help_text=_('The IP for which this address is the "outside" IP') ) dns_name = models.CharField( max_length=255, blank=True, validators=[DNSValidator], - verbose_name=_('DNS Name'), + verbose_name=_('DNS name'), help_text=_('Hostname or FQDN (not case-sensitive)') ) diff --git a/netbox/ipam/models/l2vpn.py b/netbox/ipam/models/l2vpn.py index 629a4bfb1..6234f3eea 100644 --- a/netbox/ipam/models/l2vpn.py +++ b/netbox/ipam/models/l2vpn.py @@ -128,7 +128,11 @@ class L2VPNTermination(NetBoxModel): obj_type = ContentType.objects.get_for_model(self.assigned_object) if L2VPNTermination.objects.filter(assigned_object_id=obj_id, assigned_object_type=obj_type).\ exclude(pk=self.pk).count() > 0: - raise ValidationError(_('L2VPN Termination already assigned ({assigned_object})').format(assigned_object=self.assigned_object)) + raise ValidationError( + _('L2VPN Termination already assigned ({assigned_object})').format( + assigned_object=self.assigned_object + ) + ) # Only check if L2VPN is set and is of type P2P if hasattr(self, 'l2vpn') and self.l2vpn.type in L2VPNTypeChoices.P2P: @@ -136,9 +140,10 @@ class L2VPNTermination(NetBoxModel): if terminations_count >= 2: l2vpn_type = self.l2vpn.get_type_display() raise ValidationError( - _('{l2vpn_type} L2VPNs cannot have more than two terminations; found {terminations_count} already ' - 'defined.').format(l2vpn_type=l2vpn_type, terminations_count=terminations_count) - ) + _( + '{l2vpn_type} L2VPNs cannot have more than two terminations; found {terminations_count} ' + 'already defined.' + ).format(l2vpn_type=l2vpn_type, terminations_count=terminations_count)) @property def assigned_object_parent(self): diff --git a/netbox/ipam/models/services.py b/netbox/ipam/models/services.py index 57b4154fb..277b383a5 100644 --- a/netbox/ipam/models/services.py +++ b/netbox/ipam/models/services.py @@ -30,7 +30,7 @@ class ServiceBase(models.Model): MaxValueValidator(SERVICE_PORT_MAX) ] ), - verbose_name=_('Port numbers') + verbose_name=_('port numbers') ) class Meta: @@ -82,7 +82,8 @@ class Service(ServiceBase, PrimaryModel): blank=True ) name = models.CharField( - max_length=100 + max_length=100, + verbose_name=_('name') ) ipaddresses = models.ManyToManyField( to='ipam.IPAddress', diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py index ef609cbee..3b9bd0095 100644 --- a/netbox/ipam/models/vlans.py +++ b/netbox/ipam/models/vlans.py @@ -47,7 +47,7 @@ class VLANGroup(OrganizationalModel): fk_field='scope_id' ) min_vid = models.PositiveSmallIntegerField( - verbose_name=_('Minimum VLAN ID'), + verbose_name=_('minimum VLAN ID'), default=VLAN_VID_MIN, validators=( MinValueValidator(VLAN_VID_MIN), @@ -56,7 +56,7 @@ class VLANGroup(OrganizationalModel): help_text=_('Lowest permissible ID of a child VLAN') ) max_vid = models.PositiveSmallIntegerField( - verbose_name=_('Maximum VLAN ID'), + verbose_name=_('maximum VLAN ID'), default=VLAN_VID_MAX, validators=( MinValueValidator(VLAN_VID_MIN), @@ -145,7 +145,7 @@ class VLAN(PrimaryModel): help_text=_("VLAN group (optional)") ) vid = models.PositiveSmallIntegerField( - verbose_name=_('ID'), + verbose_name=_('VLAN ID'), validators=( MinValueValidator(VLAN_VID_MIN), MaxValueValidator(VLAN_VID_MAX) @@ -219,15 +219,17 @@ class VLAN(PrimaryModel): # Validate VLAN group (if assigned) if self.group and self.site and self.group.scope != self.site: raise ValidationError({ - 'group': _("VLAN is assigned to group {group} (scope: {scope}); cannot also assign to " - "site {site}.").format(group=self.group, scope=self.group.scope, site=self.site) + 'group': _( + "VLAN is assigned to group {group} (scope: {scope}); cannot also assign to site {site}." + ).format(group=self.group, scope=self.group.scope, site=self.site) }) # Validate group min/max VIDs if self.group and not self.group.min_vid <= self.vid <= self.group.max_vid: raise ValidationError({ - 'vid': _("VID must be between {min_vid} and {max_vid} for VLANs in group " - "{group}").format(min_vid=self.group.min_vid, max_vid=self.group.max_vid, group=self.group) + 'vid': _( + "VID must be between {min_vid} and {max_vid} for VLANs in group {group}" + ).format(min_vid=self.group.min_vid, max_vid=self.group.max_vid, group=self.group) }) def get_status_color(self): diff --git a/netbox/ipam/models/vrfs.py b/netbox/ipam/models/vrfs.py index ee20f1e80..d338e2dcd 100644 --- a/netbox/ipam/models/vrfs.py +++ b/netbox/ipam/models/vrfs.py @@ -27,7 +27,7 @@ class VRF(PrimaryModel): unique=True, blank=True, null=True, - verbose_name=_('Route distinguisher'), + verbose_name=_('route distinguisher'), help_text=_('Unique route distinguisher (as defined in RFC 4364)') ) tenant = models.ForeignKey( @@ -39,7 +39,7 @@ class VRF(PrimaryModel): ) enforce_unique = models.BooleanField( default=True, - verbose_name=_('Enforce unique space'), + verbose_name=_('enforce unique space'), help_text=_('Prevent duplicate prefixes/IP addresses within this VRF') ) import_targets = models.ManyToManyField( diff --git a/netbox/netbox/models/__init__.py b/netbox/netbox/models/__init__.py index 77734e230..931d565ba 100644 --- a/netbox/netbox/models/__init__.py +++ b/netbox/netbox/models/__init__.py @@ -152,7 +152,7 @@ class NestedGroupModel(CloningMixin, NetBoxFeatureSet, MPTTModel): # An MPTT model cannot be its own parent if self.pk and self.parent and self.parent in self.get_descendants(include_self=True): raise ValidationError({ - "parent": f"Cannot assign self or child {self._meta.verbose_name} as parent." + "parent": "Cannot assign self or child {type} as parent.".format(type=self._meta.verbose_name) }) diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index e2d06c562..d5228ed63 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -146,7 +146,6 @@ class CustomFieldsMixin(models.Model): Enables support for custom fields. """ custom_field_data = models.JSONField( - verbose_name=_('custom field data'), encoder=CustomFieldJSONEncoder, blank=True, default=dict diff --git a/netbox/users/models.py b/netbox/users/models.py index a3f1fa1f7..0e2ab348d 100644 --- a/netbox/users/models.py +++ b/netbox/users/models.py @@ -105,7 +105,6 @@ class UserConfig(models.Model): related_name='config' ) data = models.JSONField( - verbose_name=_('data'), default=dict ) @@ -177,7 +176,9 @@ class UserConfig(models.Model): d = d[key] elif key in d: err_path = '.'.join(path.split('.')[:i + 1]) - raise TypeError(_("Key '{err_path}' is a leaf node; cannot assign new keys").format(err_path=err_path)) + raise TypeError( + _("Key '{err_path}' is a leaf node; cannot assign new keys").format(err_path=err_path) + ) else: d = d.setdefault(key, {}) @@ -187,7 +188,9 @@ class UserConfig(models.Model): if type(value) is dict: d[key].update(value) else: - raise TypeError(_("Key '{path}' is a dictionary; cannot assign a non-dictionary value").format(path=path)) + raise TypeError( + _("Key '{path}' is a dictionary; cannot assign a non-dictionary value").format(path=path) + ) else: d[key] = value @@ -280,7 +283,7 @@ class Token(models.Model): base_field=IPNetworkField(), blank=True, null=True, - verbose_name=_('Allowed IPs'), + verbose_name=_('allowed IPs'), help_text=_( 'Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for no restrictions. ' 'Ex: "10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64"' @@ -385,6 +388,7 @@ class ObjectPermission(models.Model): constraints = models.JSONField( blank=True, null=True, + verbose_name=_('constraints'), help_text=_("Queryset filter matching the applicable objects of the selected type(s)") ) diff --git a/netbox/virtualization/models/virtualmachines.py b/netbox/virtualization/models/virtualmachines.py index 123d72f5e..2c798ab8a 100644 --- a/netbox/virtualization/models/virtualmachines.py +++ b/netbox/virtualization/models/virtualmachines.py @@ -76,7 +76,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): max_length=50, choices=VirtualMachineStatusChoices, default=VirtualMachineStatusChoices.STATUS_ACTIVE, - verbose_name=_('Status') + verbose_name=_('status') ) role = models.ForeignKey( to='dcim.DeviceRole', @@ -92,7 +92,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): related_name='+', blank=True, null=True, - verbose_name='Primary IPv4' + verbose_name='primary IPv4' ) primary_ip6 = models.OneToOneField( to='ipam.IPAddress', @@ -100,7 +100,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): related_name='+', blank=True, null=True, - verbose_name='Primary IPv6' + verbose_name='primary IPv6' ) vcpus = models.DecimalField( max_digits=6, @@ -115,12 +115,12 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): memory = models.PositiveIntegerField( blank=True, null=True, - verbose_name=_('Memory (MB)') + verbose_name=_('memory (MB)') ) disk = models.PositiveIntegerField( blank=True, null=True, - verbose_name=_('Disk (GB)') + verbose_name=_('disk (GB)') ) # Counter fields @@ -176,7 +176,9 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): # Validate site for cluster & device if self.cluster and self.site and self.cluster.site != self.site: raise ValidationError({ - 'cluster': _('The selected cluster ({cluster}) is not assigned to this site ({site}).').format(cluster=self.cluster, site=self.site) + 'cluster': _( + 'The selected cluster ({cluster}) is not assigned to this site ({site}).' + ).format(cluster=self.cluster, site=self.site) }) # Validate assigned cluster device @@ -186,7 +188,9 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): }) if self.device and self.device not in self.cluster.devices.all(): raise ValidationError({ - 'device': _('The selected device ({device}) is not assigned to this cluster ({cluster}).').format(device=self.device, cluster=self.cluster) + 'device': _( + "The selected device ({device}) is not assigned to this cluster ({cluster})." + ).format(device=self.device, cluster=self.cluster) }) # Validate primary IP addresses @@ -197,8 +201,9 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): if ip is not None: if ip.address.version != family: raise ValidationError({ - field: _("Must be an IPv{family} address. ({ip} is an IPv{version} address.)").format( - family=family, ip=ip, version=ip.address.version), + field: _( + "Must be an IPv{family} address. ({ip} is an IPv{version} address.)" + ).format(family=family, ip=ip, version=ip.address.version) }) if ip.assigned_object in interfaces: pass @@ -259,13 +264,13 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin): related_name='vminterfaces_as_untagged', null=True, blank=True, - verbose_name=_('Untagged VLAN') + verbose_name=_('untagged VLAN') ) tagged_vlans = models.ManyToManyField( to='ipam.VLAN', related_name='vminterfaces_as_tagged', blank=True, - verbose_name=_('Tagged VLANs') + verbose_name=_('tagged VLANs') ) ip_addresses = GenericRelation( to='ipam.IPAddress', @@ -322,8 +327,10 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin): # An interface's parent must belong to the same virtual machine if self.parent and self.parent.virtual_machine != self.virtual_machine: raise ValidationError({ - 'parent': _("The selected parent interface ({parent}) belongs to a different virtual machine " - "({virtual_machine}).").format(parent=self.parent, virtual_machine=self.parent.virtual_machine) + 'parent': _( + "The selected parent interface ({parent}) belongs to a different virtual machine " + "({virtual_machine})." + ).format(parent=self.parent, virtual_machine=self.parent.virtual_machine) }) # Bridge validation @@ -335,8 +342,10 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin): # A bridged interface belong to the same virtual machine if self.bridge and self.bridge.virtual_machine != self.virtual_machine: raise ValidationError({ - 'bridge': _("The selected bridge interface ({bridge}) belongs to a different virtual machine " - "({virtual_machine}).").format(bridge=self.bridge, virtual_machine=self.bridge.virtual_machine) + 'bridge': _( + "The selected bridge interface ({bridge}) belongs to a different virtual machine " + "({virtual_machine})." + ).format(bridge=self.bridge, virtual_machine=self.bridge.virtual_machine) }) # VLAN validation @@ -344,8 +353,10 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin): # Validate untagged VLAN if self.untagged_vlan and self.untagged_vlan.site not in [self.virtual_machine.site, None]: raise ValidationError({ - 'untagged_vlan': _("The untagged VLAN ({untagged_vlan}) must belong to the same site as the " - "interface's parent virtual machine, or it must be global.").format(untagged_vlan=self.untagged_vlan) + 'untagged_vlan': _( + "The untagged VLAN ({untagged_vlan}) must belong to the same site as the interface's parent " + "virtual machine, or it must be global." + ).format(untagged_vlan=self.untagged_vlan) }) def to_objectchange(self, action): diff --git a/netbox/wireless/models.py b/netbox/wireless/models.py index b7f62485e..e49c99386 100644 --- a/netbox/wireless/models.py +++ b/netbox/wireless/models.py @@ -25,10 +25,10 @@ class WirelessAuthenticationBase(models.Model): max_length=50, choices=WirelessAuthTypeChoices, blank=True, - verbose_name=_("Auth Type"), + verbose_name=_("authentication type"), ) auth_cipher = models.CharField( - verbose_name=_('auth cipher'), + verbose_name=_('authentication cipher'), max_length=50, choices=WirelessAuthCipherChoices, blank=True @@ -36,7 +36,7 @@ class WirelessAuthenticationBase(models.Model): auth_psk = models.CharField( max_length=PSK_MAX_LENGTH, blank=True, - verbose_name=_('Pre-shared key') + verbose_name=_('pre-shared key') ) class Meta: @@ -90,7 +90,8 @@ class WirelessLAN(WirelessAuthenticationBase, PrimaryModel): status = models.CharField( max_length=50, choices=WirelessLANStatusChoices, - default=WirelessLANStatusChoices.STATUS_ACTIVE + default=WirelessLANStatusChoices.STATUS_ACTIVE, + verbose_name=_('status') ) vlan = models.ForeignKey( to='ipam.VLAN', @@ -138,14 +139,14 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel): limit_choices_to=get_wireless_interface_types, on_delete=models.PROTECT, related_name='+', - verbose_name=_('Interface A'), + verbose_name=_('interface A'), ) interface_b = models.ForeignKey( to='dcim.Interface', limit_choices_to=get_wireless_interface_types, on_delete=models.PROTECT, related_name='+', - verbose_name=_('Interface B'), + verbose_name=_('interface B'), ) ssid = models.CharField( max_length=SSID_MAX_LENGTH, @@ -208,11 +209,15 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel): # Validate interface types if self.interface_a.type not in WIRELESS_IFACE_TYPES: raise ValidationError({ - 'interface_a': _("{type_display} is not a wireless interface.").format(type_display=self.interface_a.get_type_display()) + 'interface_a': _( + "{type_display} is not a wireless interface." + ).format(type_display=self.interface_a.get_type_display()) }) if self.interface_b.type not in WIRELESS_IFACE_TYPES: raise ValidationError({ - 'interface_a': _("{type_display} is not a wireless interface.").format(type_display=self.interface_b.get_type_display()) + 'interface_a': _( + "{type_display} is not a wireless interface." + ).format(type_display=self.interface_b.get_type_display()) }) def save(self, *args, **kwargs):