From 2eb27032191195705728447d3b3582a95ce49584 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 11 Jul 2023 10:20:56 +0700 Subject: [PATCH] 13132 add gettext_lazy to models --- netbox/circuits/models/circuits.py | 30 +++++---- netbox/circuits/models/providers.py | 12 ++-- netbox/core/models/data.py | 13 +++- netbox/core/models/files.py | 6 +- netbox/core/models/jobs.py | 11 +++- netbox/dcim/models/cables.py | 13 +++- .../dcim/models/device_component_templates.py | 66 ++++++++++++------- netbox/netbox/models/__init__.py | 9 +++ netbox/netbox/models/features.py | 8 ++- 9 files changed, 121 insertions(+), 47 deletions(-) diff --git a/netbox/circuits/models/circuits.py b/netbox/circuits/models/circuits.py index f629c0b30..667eab1cb 100644 --- a/netbox/circuits/models/circuits.py +++ b/netbox/circuits/models/circuits.py @@ -2,7 +2,7 @@ from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from circuits.choices import * from dcim.models import CabledObjectModel @@ -34,8 +34,8 @@ class Circuit(PrimaryModel): """ cid = models.CharField( max_length=100, - verbose_name='Circuit ID', - help_text=_("Unique circuit ID") + verbose_name=_('Circuit ID'), + help_text=_('Unique circuit ID') ) provider = models.ForeignKey( to='circuits.Provider', @@ -55,6 +55,7 @@ class Circuit(PrimaryModel): related_name='circuits' ) status = models.CharField( + verbose_name=_('status'), max_length=50, choices=CircuitStatusChoices, default=CircuitStatusChoices.STATUS_ACTIVE @@ -69,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") ) @@ -162,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', @@ -179,30 +180,31 @@ 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") + help_text=_('Physical circuit speed') ) 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', - help_text=_("ID of the local cross-connect") + 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)', - help_text=_("Patch panel ID and port number(s)") + verbose_name=_('Patch panel/port(s)'), + help_text=_('Patch panel ID and port number(s)') ) description = models.CharField( + verbose_name=_('description'), max_length=200, blank=True ) diff --git a/netbox/circuits/models/providers.py b/netbox/circuits/models/providers.py index 52eb26c98..de946798f 100644 --- a/netbox/circuits/models/providers.py +++ b/netbox/circuits/models/providers.py @@ -2,7 +2,7 @@ from django.contrib.contenttypes.fields import GenericRelation from django.db import models from django.db.models import Q from django.urls import reverse -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from netbox.models import PrimaryModel @@ -19,11 +19,13 @@ class Provider(PrimaryModel): stores information pertinent to the user's relationship with the Provider. """ name = models.CharField( + verbose_name=_('name'), max_length=100, unique=True, - help_text=_("Full name of the provider") + help_text=_('Full name of the provider') ) slug = models.SlugField( + verbose_name=_('slug'), max_length=100, unique=True ) @@ -61,9 +63,10 @@ class ProviderAccount(PrimaryModel): ) account = models.CharField( max_length=100, - verbose_name='Account ID' + verbose_name=_('Account ID') ) name = models.CharField( + verbose_name=_('name'), max_length=100, blank=True ) @@ -104,6 +107,7 @@ class ProviderNetwork(PrimaryModel): unimportant to the user. """ name = models.CharField( + verbose_name=_('name'), max_length=100 ) provider = models.ForeignKey( @@ -114,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/core/models/data.py b/netbox/core/models/data.py index a2a20f858..6ee0324c8 100644 --- a/netbox/core/models/data.py +++ b/netbox/core/models/data.py @@ -39,10 +39,12 @@ class DataSource(JobsMixin, PrimaryModel): A remote source, such as a git repository, from which DataFiles are synchronized. """ name = models.CharField( + verbose_name=_('name'), max_length=100, unique=True ) type = models.CharField( + verbose_name=_('type'), max_length=50, choices=DataSourceTypeChoices, default=DataSourceTypeChoices.LOCAL @@ -52,23 +54,28 @@ class DataSource(JobsMixin, PrimaryModel): verbose_name=_('URL') ) status = models.CharField( + verbose_name=_('status'), max_length=50, choices=DataSourceStatusChoices, default=DataSourceStatusChoices.NEW, editable=False ) enabled = models.BooleanField( + verbose_name=_('enabled'), default=True ) ignore_rules = models.TextField( + verbose_name=_('ignore rules'), blank=True, help_text=_("Patterns (one per line) matching files to ignore when syncing") ) parameters = models.JSONField( + verbose_name=_('parameters'), blank=True, null=True ) last_synced = models.DateTimeField( + verbose_name=_('last synced'), blank=True, null=True, editable=False @@ -239,9 +246,11 @@ class DataFile(models.Model): updated, or deleted only by calling DataSource.sync(). """ created = models.DateTimeField( + verbose_name=_('created'), auto_now_add=True ) last_updated = models.DateTimeField( + verbose_name=_('last updated'), editable=False ) source = models.ForeignKey( @@ -251,6 +260,7 @@ class DataFile(models.Model): editable=False ) path = models.CharField( + verbose_name=_('path'), max_length=1000, editable=False, help_text=_("File path relative to the data source's root") @@ -259,12 +269,13 @@ class DataFile(models.Model): editable=False ) hash = models.CharField( + verbose_name=_('hash'), max_length=64, editable=False, validators=[ RegexValidator(regex='^[0-9a-f]{64}$', message=_("Length must be 64 hexadecimal characters.")) ], - help_text=_("SHA256 hash of the file data") + help_text=_('SHA256 hash of the file data') ) data = models.BinaryField() diff --git a/netbox/core/models/files.py b/netbox/core/models/files.py index a725ea0ac..c70f4c4fa 100644 --- a/netbox/core/models/files.py +++ b/netbox/core/models/files.py @@ -23,20 +23,24 @@ class ManagedFile(SyncedDataMixin, models.Model): to provide additional functionality. """ created = models.DateTimeField( + verbose_name=_('created'), auto_now_add=True ) last_updated = models.DateTimeField( + verbose_name=_('last updated'), editable=False, blank=True, null=True ) file_root = models.CharField( + verbose_name=_('file root'), max_length=1000, choices=ManagedFileRootPathChoices ) file_path = models.FilePathField( + verbose_name=_('file path'), editable=False, - help_text=_("File path relative to the designated root path") + help_text=_('File path relative to the designated root path') ) objects = RestrictedQuerySet.as_manager() diff --git a/netbox/core/models/jobs.py b/netbox/core/models/jobs.py index 9be06bd6d..eda9f7bd0 100644 --- a/netbox/core/models/jobs.py +++ b/netbox/core/models/jobs.py @@ -43,28 +43,34 @@ class Job(models.Model): for_concrete_model=False ) name = models.CharField( + verbose_name=_('name'), max_length=200 ) created = models.DateTimeField( + verbose_name=_('created'), auto_now_add=True ) scheduled = models.DateTimeField( + verbose_name=_('scheduled'), null=True, blank=True ) interval = models.PositiveIntegerField( + verbose_name=_('interval'), blank=True, null=True, validators=( MinValueValidator(1), ), - help_text=_("Recurrence interval (in minutes)") + help_text=_('Recurrence interval (in minutes)') ) started = models.DateTimeField( + verbose_name=_('started'), null=True, blank=True ) completed = models.DateTimeField( + verbose_name=_('completed'), null=True, blank=True ) @@ -76,15 +82,18 @@ class Job(models.Model): null=True ) status = models.CharField( + verbose_name=_('status'), max_length=30, choices=JobStatusChoices, default=JobStatusChoices.STATUS_PENDING ) data = models.JSONField( + verbose_name=_('data'), null=True, blank=True ) job_id = models.UUIDField( + verbose_name=_('job id'), unique=True ) diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index b2786719c..8325d0acc 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -8,6 +8,7 @@ from django.db import models from django.db.models import Sum from django.dispatch import Signal from django.urls import reverse +from django.utils.translation import gettext_lazy as _ from dcim.choices import * from dcim.constants import * @@ -40,11 +41,13 @@ class Cable(PrimaryModel): A physical connection between two endpoints. """ type = models.CharField( + verbose_name=_('type'), max_length=50, choices=CableTypeChoices, blank=True ) status = models.CharField( + verbose_name=_('status'), max_length=50, choices=LinkStatusChoices, default=LinkStatusChoices.STATUS_CONNECTED @@ -57,19 +60,23 @@ class Cable(PrimaryModel): null=True ) label = models.CharField( + verbose_name=_('label'), max_length=100, blank=True ) color = ColorField( + verbose_name=_('color'), blank=True ) length = models.DecimalField( + verbose_name=_('length'), max_digits=8, decimal_places=2, blank=True, null=True ) length_unit = models.CharField( + verbose_name=_('length unit'), max_length=50, choices=CableLengthUnitChoices, blank=True, @@ -235,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, @@ -403,15 +410,19 @@ class CablePath(models.Model): `_nodes` retains a flattened list of all nodes within the path to enable simple filtering. """ path = models.JSONField( + verbose_name=_('path'), default=list ) is_active = models.BooleanField( + verbose_name=_('is active'), default=False ) is_complete = models.BooleanField( + verbose_name=_('is complete'), default=False ) is_split = models.BooleanField( + verbose_name=_('is split'), default=False ) _nodes = PathField() diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index d4539a6ab..113044129 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -3,7 +3,7 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from mptt.models import MPTTModel, TreeForeignKey from dcim.choices import * @@ -41,10 +41,11 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin): related_name='%(class)ss' ) name = models.CharField( + verbose_name=_('name'), max_length=64, - help_text=""" + help_text=_(""" {module} is accepted as a substitution for the module bay position when attached to a module type. - """ + """) ) _name = NaturalOrderingField( target_field='name', @@ -52,9 +53,10 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin): blank=True ) label = models.CharField( + verbose_name=_('label'), max_length=64, blank=True, - help_text=_("Physical label") + help_text=_('Physical label') ) description = models.CharField( max_length=200, @@ -98,7 +100,7 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin): if self.pk is not None and self._original_device_type != self.device_type_id: raise ValidationError({ - "device_type": "Component templates cannot be moved to a different device type." + "device_type": _("Component templates cannot be moved to a different device type.") }) @@ -149,11 +151,11 @@ class ModularComponentTemplateModel(ComponentTemplateModel): # A component template must belong to a DeviceType *or* to a ModuleType if self.device_type and self.module_type: raise ValidationError( - "A component template cannot be associated with both a device type and a module type." + _("A component template cannot be associated with both a device type and a module type.") ) if not self.device_type and not self.module_type: raise ValidationError( - "A component template must be associated with either a device type or a module type." + _("A component template must be associated with either a device type or a module type.") ) def resolve_name(self, module): @@ -172,6 +174,7 @@ class ConsolePortTemplate(ModularComponentTemplateModel): A template for a ConsolePort to be created for a new Device. """ type = models.CharField( + verbose_name=_('type'), max_length=50, choices=ConsolePortTypeChoices, blank=True @@ -201,6 +204,7 @@ class ConsoleServerPortTemplate(ModularComponentTemplateModel): A template for a ConsoleServerPort to be created for a new Device. """ type = models.CharField( + verbose_name=_('type'), max_length=50, choices=ConsolePortTypeChoices, blank=True @@ -231,21 +235,24 @@ class PowerPortTemplate(ModularComponentTemplateModel): A template for a PowerPort to be created for a new Device. """ type = models.CharField( + verbose_name=_('type'), max_length=50, choices=PowerPortTypeChoices, blank=True ) maximum_draw = models.PositiveIntegerField( + verbose_name=_('maximum draw'), blank=True, null=True, validators=[MinValueValidator(1)], - help_text=_("Maximum power draw (watts)") + help_text=_('Maximum power draw (watts)') ) allocated_draw = models.PositiveIntegerField( + verbose_name=_('allocated draw'), blank=True, null=True, validators=[MinValueValidator(1)], - help_text=_("Allocated power draw (watts)") + help_text=_('Allocated power draw (watts)') ) component_model = PowerPort @@ -267,7 +274,7 @@ class PowerPortTemplate(ModularComponentTemplateModel): 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': f"Allocated draw cannot exceed the maximum draw ({self.maximum_draw}W)." + 'allocated_draw': _("Allocated draw cannot exceed the maximum draw ({maximum_draw}W).").format(maximum_draw=self.maximum_draw) }) def to_yaml(self): @@ -286,6 +293,7 @@ class PowerOutletTemplate(ModularComponentTemplateModel): A template for a PowerOutlet to be created for a new Device. """ type = models.CharField( + verbose_name=_('type'), max_length=50, choices=PowerOutletTypeChoices, blank=True @@ -298,10 +306,11 @@ class PowerOutletTemplate(ModularComponentTemplateModel): related_name='poweroutlet_templates' ) feed_leg = models.CharField( + verbose_name=_('feed leg'), max_length=50, choices=PowerOutletFeedLegChoices, blank=True, - help_text=_("Phase (for three-phase feeds)") + help_text=_('Phase (for three-phase feeds)') ) component_model = PowerOutlet @@ -313,11 +322,11 @@ class PowerOutletTemplate(ModularComponentTemplateModel): if self.power_port: if self.device_type and self.power_port.device_type != self.device_type: raise ValidationError( - f"Parent power port ({self.power_port}) must belong to the same device type" + _("Parent power port ({power_port}) must belong to the same device type").format(power_port=self.power_port) ) if self.module_type and self.power_port.module_type != self.module_type: raise ValidationError( - f"Parent power port ({self.power_port}) must belong to the same module type" + _("Parent power port ({power_port}) must belong to the same module type").format(power_port=self.power_port) ) def instantiate(self, **kwargs): @@ -359,15 +368,17 @@ class InterfaceTemplate(ModularComponentTemplateModel): blank=True ) type = models.CharField( + verbose_name=_('type'), max_length=50, choices=InterfaceTypeChoices ) enabled = models.BooleanField( + verbose_name=_('enabled'), default=True ) mgmt_only = models.BooleanField( default=False, - verbose_name='Management only' + verbose_name=_('Management only') ) bridge = models.ForeignKey( to='self', @@ -375,19 +386,19 @@ 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, choices=InterfacePoEModeChoices, blank=True, - verbose_name='PoE mode' + verbose_name=_('PoE mode') ) poe_type = models.CharField( max_length=50, choices=InterfacePoETypeChoices, blank=True, - verbose_name='PoE type' + verbose_name=_('PoE type') ) rf_role = models.CharField( max_length=30, @@ -403,14 +414,14 @@ class InterfaceTemplate(ModularComponentTemplateModel): if self.bridge: if self.pk and self.bridge_id == self.pk: - raise ValidationError({'bridge': "An interface cannot be bridged to itself."}) + raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")}) if self.device_type and self.device_type != self.bridge.device_type: raise ValidationError({ - 'bridge': f"Bridge interface ({self.bridge}) must belong to the same device type" + 'bridge': _("Bridge interface ({bridge}) must belong to the same device type").format(bridge=self.bridge) }) if self.module_type and self.module_type != self.bridge.module_type: raise ValidationError({ - 'bridge': f"Bridge interface ({self.bridge}) must belong to the same module type" + 'bridge': _("Bridge interface ({bridge}) must belong to the same module type").format(bridge=self.bridge) }) if self.rf_role and self.type not in WIRELESS_IFACE_TYPES: @@ -452,10 +463,12 @@ class FrontPortTemplate(ModularComponentTemplateModel): Template for a pass-through port on the front of a new Device. """ type = models.CharField( + verbose_name=_('type'), max_length=50, choices=PortTypeChoices ) color = ColorField( + verbose_name=_('color'), blank=True ) rear_port = models.ForeignKey( @@ -464,6 +477,7 @@ class FrontPortTemplate(ModularComponentTemplateModel): related_name='frontport_templates' ) rear_port_position = models.PositiveSmallIntegerField( + verbose_name=_('rear port position'), default=1, validators=[ MinValueValidator(REARPORT_POSITIONS_MIN), @@ -497,13 +511,13 @@ class FrontPortTemplate(ModularComponentTemplateModel): # Validate rear port assignment if self.rear_port.device_type != self.device_type: raise ValidationError( - "Rear port ({}) must belong to the same device type".format(self.rear_port) + _("Rear port ({}) must belong to the same device type").format(self.rear_port) ) # Validate rear port position assignment if self.rear_port_position > self.rear_port.positions: raise ValidationError( - "Invalid rear port position ({}); rear port {} has only {} positions".format( + _("Invalid rear port position ({}); rear port {} has only {} positions").format( self.rear_port_position, self.rear_port.name, self.rear_port.positions ) ) @@ -545,13 +559,16 @@ class RearPortTemplate(ModularComponentTemplateModel): Template for a pass-through port on the rear of a new Device. """ type = models.CharField( + verbose_name=_('type'), max_length=50, choices=PortTypeChoices ) color = ColorField( + verbose_name=_('color'), blank=True ) positions = models.PositiveSmallIntegerField( + verbose_name=_('positions'), default=1, validators=[ MinValueValidator(REARPORT_POSITIONS_MIN), @@ -588,6 +605,7 @@ class ModuleBayTemplate(ComponentTemplateModel): A template for a ModuleBay to be created for a new parent Device. """ position = models.CharField( + verbose_name=_('position'), max_length=30, blank=True, help_text=_('Identifier to reference when renaming installed components') @@ -630,7 +648,7 @@ class DeviceBayTemplate(ComponentTemplateModel): def clean(self): if self.device_type and self.device_type.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT: raise ValidationError( - f"Subdevice role of device type ({self.device_type}) must be set to \"parent\" to allow device bays." + _("Subdevice role of device type ({device_type}) must be set to \"parent\" to allow device bays.").format(device_type=self.device_type) ) def to_yaml(self): @@ -685,7 +703,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/netbox/models/__init__.py b/netbox/netbox/models/__init__.py index 23dcfb985..77734e230 100644 --- a/netbox/netbox/models/__init__.py +++ b/netbox/netbox/models/__init__.py @@ -2,6 +2,7 @@ from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey from django.core.validators import ValidationError from django.db import models +from django.utils.translation import gettext_lazy as _ from mptt.models import MPTTModel, TreeForeignKey from netbox.models.features import * @@ -94,10 +95,12 @@ class PrimaryModel(NetBoxModel): Primary models represent real objects within the infrastructure being modeled. """ description = models.CharField( + verbose_name=_('description'), max_length=200, blank=True ) comments = models.TextField( + verbose_name=_('comments'), blank=True ) @@ -119,12 +122,15 @@ class NestedGroupModel(CloningMixin, NetBoxFeatureSet, MPTTModel): db_index=True ) name = models.CharField( + verbose_name=_('name'), max_length=100 ) slug = models.SlugField( + verbose_name=_('slug'), max_length=100 ) description = models.CharField( + verbose_name=_('description'), max_length=200, blank=True ) @@ -160,14 +166,17 @@ class OrganizationalModel(NetBoxFeatureSet, models.Model): - Optional description """ name = models.CharField( + verbose_name=_('name'), max_length=100, unique=True ) slug = models.SlugField( + verbose_name=_('slug'), max_length=100, unique=True ) description = models.CharField( + verbose_name=_('description'), max_length=200, blank=True ) diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index e07857145..e2d06c562 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -9,7 +9,7 @@ from django.db import models from django.db.models.signals import class_prepared from django.dispatch import receiver from django.utils import timezone -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from taggit.managers import TaggableManager from core.choices import JobStatusChoices @@ -46,11 +46,13 @@ class ChangeLoggingMixin(models.Model): Provides change logging support for a model. Adds the `created` and `last_updated` fields. """ created = models.DateTimeField( + verbose_name=_('created'), auto_now_add=True, blank=True, null=True ) last_updated = models.DateTimeField( + verbose_name=_('last updated'), auto_now=True, blank=True, null=True @@ -144,6 +146,7 @@ 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 @@ -401,16 +404,19 @@ class SyncedDataMixin(models.Model): related_name='+' ) data_path = models.CharField( + verbose_name=_('data path'), max_length=1000, blank=True, editable=False, help_text=_("Path to remote file (relative to data source root)") ) auto_sync_enabled = models.BooleanField( + verbose_name=_('auto sync enabled'), default=False, help_text=_("Enable automatic synchronization of data when the data file is updated") ) data_synced = models.DateTimeField( + verbose_name=_('date synced'), blank=True, null=True, editable=False