13132 add gettext_lazy to models

This commit is contained in:
Arthur 2023-07-11 10:20:56 +07:00 committed by Jeremy Stretch
parent 0c2e3ff898
commit 2eb2703219
9 changed files with 121 additions and 47 deletions

View File

@ -2,7 +2,7 @@ from django.contrib.contenttypes.fields import GenericRelation
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.urls import reverse 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 circuits.choices import *
from dcim.models import CabledObjectModel from dcim.models import CabledObjectModel
@ -34,8 +34,8 @@ class Circuit(PrimaryModel):
""" """
cid = models.CharField( cid = models.CharField(
max_length=100, max_length=100,
verbose_name='Circuit ID', verbose_name=_('Circuit ID'),
help_text=_("Unique circuit ID") help_text=_('Unique circuit ID')
) )
provider = models.ForeignKey( provider = models.ForeignKey(
to='circuits.Provider', to='circuits.Provider',
@ -55,6 +55,7 @@ class Circuit(PrimaryModel):
related_name='circuits' related_name='circuits'
) )
status = models.CharField( status = models.CharField(
verbose_name=_('status'),
max_length=50, max_length=50,
choices=CircuitStatusChoices, choices=CircuitStatusChoices,
default=CircuitStatusChoices.STATUS_ACTIVE default=CircuitStatusChoices.STATUS_ACTIVE
@ -69,17 +70,17 @@ class Circuit(PrimaryModel):
install_date = models.DateField( install_date = models.DateField(
blank=True, blank=True,
null=True, null=True,
verbose_name='Installed' verbose_name=_('Installed')
) )
termination_date = models.DateField( termination_date = models.DateField(
blank=True, blank=True,
null=True, null=True,
verbose_name='Terminates' verbose_name=_('Terminates')
) )
commit_rate = models.PositiveIntegerField( commit_rate = models.PositiveIntegerField(
blank=True, blank=True,
null=True, null=True,
verbose_name='Commit rate (Kbps)', verbose_name=_('Commit rate (Kbps)'),
help_text=_("Committed rate") help_text=_("Committed rate")
) )
@ -162,7 +163,7 @@ class CircuitTermination(
term_side = models.CharField( term_side = models.CharField(
max_length=1, max_length=1,
choices=CircuitTerminationSideChoices, choices=CircuitTerminationSideChoices,
verbose_name='Termination' verbose_name=_('Termination')
) )
site = models.ForeignKey( site = models.ForeignKey(
to='dcim.Site', to='dcim.Site',
@ -179,30 +180,31 @@ class CircuitTermination(
null=True null=True
) )
port_speed = models.PositiveIntegerField( port_speed = models.PositiveIntegerField(
verbose_name='Port speed (Kbps)', verbose_name=_('Port speed (Kbps)'),
blank=True, blank=True,
null=True, null=True,
help_text=_("Physical circuit speed") help_text=_('Physical circuit speed')
) )
upstream_speed = models.PositiveIntegerField( upstream_speed = models.PositiveIntegerField(
blank=True, blank=True,
null=True, null=True,
verbose_name='Upstream speed (Kbps)', verbose_name=_('Upstream speed (Kbps)'),
help_text=_('Upstream speed, if different from port speed') help_text=_('Upstream speed, if different from port speed')
) )
xconnect_id = models.CharField( xconnect_id = models.CharField(
max_length=50, max_length=50,
blank=True, blank=True,
verbose_name='Cross-connect ID', verbose_name=_('Cross-connect ID'),
help_text=_("ID of the local cross-connect") help_text=_('ID of the local cross-connect')
) )
pp_info = models.CharField( pp_info = models.CharField(
max_length=100, max_length=100,
blank=True, blank=True,
verbose_name='Patch panel/port(s)', verbose_name=_('Patch panel/port(s)'),
help_text=_("Patch panel ID and port number(s)") help_text=_('Patch panel ID and port number(s)')
) )
description = models.CharField( description = models.CharField(
verbose_name=_('description'),
max_length=200, max_length=200,
blank=True blank=True
) )

View File

@ -2,7 +2,7 @@ from django.contrib.contenttypes.fields import GenericRelation
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import Q
from django.urls import reverse 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 from netbox.models import PrimaryModel
@ -19,11 +19,13 @@ class Provider(PrimaryModel):
stores information pertinent to the user's relationship with the Provider. stores information pertinent to the user's relationship with the Provider.
""" """
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=100, max_length=100,
unique=True, unique=True,
help_text=_("Full name of the provider") help_text=_('Full name of the provider')
) )
slug = models.SlugField( slug = models.SlugField(
verbose_name=_('slug'),
max_length=100, max_length=100,
unique=True unique=True
) )
@ -61,9 +63,10 @@ class ProviderAccount(PrimaryModel):
) )
account = models.CharField( account = models.CharField(
max_length=100, max_length=100,
verbose_name='Account ID' verbose_name=_('Account ID')
) )
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=100, max_length=100,
blank=True blank=True
) )
@ -104,6 +107,7 @@ class ProviderNetwork(PrimaryModel):
unimportant to the user. unimportant to the user.
""" """
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=100 max_length=100
) )
provider = models.ForeignKey( provider = models.ForeignKey(
@ -114,7 +118,7 @@ class ProviderNetwork(PrimaryModel):
service_id = models.CharField( service_id = models.CharField(
max_length=100, max_length=100,
blank=True, blank=True,
verbose_name='Service ID' verbose_name=_('Service ID')
) )
class Meta: class Meta:

View File

@ -39,10 +39,12 @@ class DataSource(JobsMixin, PrimaryModel):
A remote source, such as a git repository, from which DataFiles are synchronized. A remote source, such as a git repository, from which DataFiles are synchronized.
""" """
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=100, max_length=100,
unique=True unique=True
) )
type = models.CharField( type = models.CharField(
verbose_name=_('type'),
max_length=50, max_length=50,
choices=DataSourceTypeChoices, choices=DataSourceTypeChoices,
default=DataSourceTypeChoices.LOCAL default=DataSourceTypeChoices.LOCAL
@ -52,23 +54,28 @@ class DataSource(JobsMixin, PrimaryModel):
verbose_name=_('URL') verbose_name=_('URL')
) )
status = models.CharField( status = models.CharField(
verbose_name=_('status'),
max_length=50, max_length=50,
choices=DataSourceStatusChoices, choices=DataSourceStatusChoices,
default=DataSourceStatusChoices.NEW, default=DataSourceStatusChoices.NEW,
editable=False editable=False
) )
enabled = models.BooleanField( enabled = models.BooleanField(
verbose_name=_('enabled'),
default=True default=True
) )
ignore_rules = models.TextField( ignore_rules = models.TextField(
verbose_name=_('ignore rules'),
blank=True, blank=True,
help_text=_("Patterns (one per line) matching files to ignore when syncing") help_text=_("Patterns (one per line) matching files to ignore when syncing")
) )
parameters = models.JSONField( parameters = models.JSONField(
verbose_name=_('parameters'),
blank=True, blank=True,
null=True null=True
) )
last_synced = models.DateTimeField( last_synced = models.DateTimeField(
verbose_name=_('last synced'),
blank=True, blank=True,
null=True, null=True,
editable=False editable=False
@ -239,9 +246,11 @@ class DataFile(models.Model):
updated, or deleted only by calling DataSource.sync(). updated, or deleted only by calling DataSource.sync().
""" """
created = models.DateTimeField( created = models.DateTimeField(
verbose_name=_('created'),
auto_now_add=True auto_now_add=True
) )
last_updated = models.DateTimeField( last_updated = models.DateTimeField(
verbose_name=_('last updated'),
editable=False editable=False
) )
source = models.ForeignKey( source = models.ForeignKey(
@ -251,6 +260,7 @@ class DataFile(models.Model):
editable=False editable=False
) )
path = models.CharField( path = models.CharField(
verbose_name=_('path'),
max_length=1000, max_length=1000,
editable=False, editable=False,
help_text=_("File path relative to the data source's root") help_text=_("File path relative to the data source's root")
@ -259,12 +269,13 @@ class DataFile(models.Model):
editable=False editable=False
) )
hash = models.CharField( hash = models.CharField(
verbose_name=_('hash'),
max_length=64, max_length=64,
editable=False, editable=False,
validators=[ validators=[
RegexValidator(regex='^[0-9a-f]{64}$', message=_("Length must be 64 hexadecimal characters.")) 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() data = models.BinaryField()

View File

@ -23,20 +23,24 @@ class ManagedFile(SyncedDataMixin, models.Model):
to provide additional functionality. to provide additional functionality.
""" """
created = models.DateTimeField( created = models.DateTimeField(
verbose_name=_('created'),
auto_now_add=True auto_now_add=True
) )
last_updated = models.DateTimeField( last_updated = models.DateTimeField(
verbose_name=_('last updated'),
editable=False, editable=False,
blank=True, blank=True,
null=True null=True
) )
file_root = models.CharField( file_root = models.CharField(
verbose_name=_('file root'),
max_length=1000, max_length=1000,
choices=ManagedFileRootPathChoices choices=ManagedFileRootPathChoices
) )
file_path = models.FilePathField( file_path = models.FilePathField(
verbose_name=_('file path'),
editable=False, 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() objects = RestrictedQuerySet.as_manager()

View File

@ -43,28 +43,34 @@ class Job(models.Model):
for_concrete_model=False for_concrete_model=False
) )
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=200 max_length=200
) )
created = models.DateTimeField( created = models.DateTimeField(
verbose_name=_('created'),
auto_now_add=True auto_now_add=True
) )
scheduled = models.DateTimeField( scheduled = models.DateTimeField(
verbose_name=_('scheduled'),
null=True, null=True,
blank=True blank=True
) )
interval = models.PositiveIntegerField( interval = models.PositiveIntegerField(
verbose_name=_('interval'),
blank=True, blank=True,
null=True, null=True,
validators=( validators=(
MinValueValidator(1), MinValueValidator(1),
), ),
help_text=_("Recurrence interval (in minutes)") help_text=_('Recurrence interval (in minutes)')
) )
started = models.DateTimeField( started = models.DateTimeField(
verbose_name=_('started'),
null=True, null=True,
blank=True blank=True
) )
completed = models.DateTimeField( completed = models.DateTimeField(
verbose_name=_('completed'),
null=True, null=True,
blank=True blank=True
) )
@ -76,15 +82,18 @@ class Job(models.Model):
null=True null=True
) )
status = models.CharField( status = models.CharField(
verbose_name=_('status'),
max_length=30, max_length=30,
choices=JobStatusChoices, choices=JobStatusChoices,
default=JobStatusChoices.STATUS_PENDING default=JobStatusChoices.STATUS_PENDING
) )
data = models.JSONField( data = models.JSONField(
verbose_name=_('data'),
null=True, null=True,
blank=True blank=True
) )
job_id = models.UUIDField( job_id = models.UUIDField(
verbose_name=_('job id'),
unique=True unique=True
) )

View File

@ -8,6 +8,7 @@ from django.db import models
from django.db.models import Sum from django.db.models import Sum
from django.dispatch import Signal from django.dispatch import Signal
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from dcim.choices import * from dcim.choices import *
from dcim.constants import * from dcim.constants import *
@ -40,11 +41,13 @@ class Cable(PrimaryModel):
A physical connection between two endpoints. A physical connection between two endpoints.
""" """
type = models.CharField( type = models.CharField(
verbose_name=_('type'),
max_length=50, max_length=50,
choices=CableTypeChoices, choices=CableTypeChoices,
blank=True blank=True
) )
status = models.CharField( status = models.CharField(
verbose_name=_('status'),
max_length=50, max_length=50,
choices=LinkStatusChoices, choices=LinkStatusChoices,
default=LinkStatusChoices.STATUS_CONNECTED default=LinkStatusChoices.STATUS_CONNECTED
@ -57,19 +60,23 @@ class Cable(PrimaryModel):
null=True null=True
) )
label = models.CharField( label = models.CharField(
verbose_name=_('label'),
max_length=100, max_length=100,
blank=True blank=True
) )
color = ColorField( color = ColorField(
verbose_name=_('color'),
blank=True blank=True
) )
length = models.DecimalField( length = models.DecimalField(
verbose_name=_('length'),
max_digits=8, max_digits=8,
decimal_places=2, decimal_places=2,
blank=True, blank=True,
null=True null=True
) )
length_unit = models.CharField( length_unit = models.CharField(
verbose_name=_('length unit'),
max_length=50, max_length=50,
choices=CableLengthUnitChoices, choices=CableLengthUnitChoices,
blank=True, blank=True,
@ -235,7 +242,7 @@ class CableTermination(ChangeLoggedModel):
cable_end = models.CharField( cable_end = models.CharField(
max_length=1, max_length=1,
choices=CableEndChoices, choices=CableEndChoices,
verbose_name='End' verbose_name=_('End')
) )
termination_type = models.ForeignKey( termination_type = models.ForeignKey(
to=ContentType, 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. `_nodes` retains a flattened list of all nodes within the path to enable simple filtering.
""" """
path = models.JSONField( path = models.JSONField(
verbose_name=_('path'),
default=list default=list
) )
is_active = models.BooleanField( is_active = models.BooleanField(
verbose_name=_('is active'),
default=False default=False
) )
is_complete = models.BooleanField( is_complete = models.BooleanField(
verbose_name=_('is complete'),
default=False default=False
) )
is_split = models.BooleanField( is_split = models.BooleanField(
verbose_name=_('is split'),
default=False default=False
) )
_nodes = PathField() _nodes = PathField()

View File

@ -3,7 +3,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models 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 mptt.models import MPTTModel, TreeForeignKey
from dcim.choices import * from dcim.choices import *
@ -41,10 +41,11 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin):
related_name='%(class)ss' related_name='%(class)ss'
) )
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=64, max_length=64,
help_text=""" help_text=_("""
{module} is accepted as a substitution for the module bay position when attached to a module type. {module} is accepted as a substitution for the module bay position when attached to a module type.
""" """)
) )
_name = NaturalOrderingField( _name = NaturalOrderingField(
target_field='name', target_field='name',
@ -52,9 +53,10 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin):
blank=True blank=True
) )
label = models.CharField( label = models.CharField(
verbose_name=_('label'),
max_length=64, max_length=64,
blank=True, blank=True,
help_text=_("Physical label") help_text=_('Physical label')
) )
description = models.CharField( description = models.CharField(
max_length=200, 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: if self.pk is not None and self._original_device_type != self.device_type_id:
raise ValidationError({ 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 # A component template must belong to a DeviceType *or* to a ModuleType
if self.device_type and self.module_type: if self.device_type and self.module_type:
raise ValidationError( 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: if not self.device_type and not self.module_type:
raise ValidationError( 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): def resolve_name(self, module):
@ -172,6 +174,7 @@ class ConsolePortTemplate(ModularComponentTemplateModel):
A template for a ConsolePort to be created for a new Device. A template for a ConsolePort to be created for a new Device.
""" """
type = models.CharField( type = models.CharField(
verbose_name=_('type'),
max_length=50, max_length=50,
choices=ConsolePortTypeChoices, choices=ConsolePortTypeChoices,
blank=True blank=True
@ -201,6 +204,7 @@ class ConsoleServerPortTemplate(ModularComponentTemplateModel):
A template for a ConsoleServerPort to be created for a new Device. A template for a ConsoleServerPort to be created for a new Device.
""" """
type = models.CharField( type = models.CharField(
verbose_name=_('type'),
max_length=50, max_length=50,
choices=ConsolePortTypeChoices, choices=ConsolePortTypeChoices,
blank=True blank=True
@ -231,21 +235,24 @@ class PowerPortTemplate(ModularComponentTemplateModel):
A template for a PowerPort to be created for a new Device. A template for a PowerPort to be created for a new Device.
""" """
type = models.CharField( type = models.CharField(
verbose_name=_('type'),
max_length=50, max_length=50,
choices=PowerPortTypeChoices, choices=PowerPortTypeChoices,
blank=True blank=True
) )
maximum_draw = models.PositiveIntegerField( maximum_draw = models.PositiveIntegerField(
verbose_name=_('maximum draw'),
blank=True, blank=True,
null=True, null=True,
validators=[MinValueValidator(1)], validators=[MinValueValidator(1)],
help_text=_("Maximum power draw (watts)") help_text=_('Maximum power draw (watts)')
) )
allocated_draw = models.PositiveIntegerField( allocated_draw = models.PositiveIntegerField(
verbose_name=_('allocated draw'),
blank=True, blank=True,
null=True, null=True,
validators=[MinValueValidator(1)], validators=[MinValueValidator(1)],
help_text=_("Allocated power draw (watts)") help_text=_('Allocated power draw (watts)')
) )
component_model = PowerPort 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.maximum_draw is not None and self.allocated_draw is not None:
if self.allocated_draw > self.maximum_draw: if self.allocated_draw > self.maximum_draw:
raise ValidationError({ 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): def to_yaml(self):
@ -286,6 +293,7 @@ class PowerOutletTemplate(ModularComponentTemplateModel):
A template for a PowerOutlet to be created for a new Device. A template for a PowerOutlet to be created for a new Device.
""" """
type = models.CharField( type = models.CharField(
verbose_name=_('type'),
max_length=50, max_length=50,
choices=PowerOutletTypeChoices, choices=PowerOutletTypeChoices,
blank=True blank=True
@ -298,10 +306,11 @@ class PowerOutletTemplate(ModularComponentTemplateModel):
related_name='poweroutlet_templates' related_name='poweroutlet_templates'
) )
feed_leg = models.CharField( feed_leg = models.CharField(
verbose_name=_('feed leg'),
max_length=50, max_length=50,
choices=PowerOutletFeedLegChoices, choices=PowerOutletFeedLegChoices,
blank=True, blank=True,
help_text=_("Phase (for three-phase feeds)") help_text=_('Phase (for three-phase feeds)')
) )
component_model = PowerOutlet component_model = PowerOutlet
@ -313,11 +322,11 @@ class PowerOutletTemplate(ModularComponentTemplateModel):
if self.power_port: if self.power_port:
if self.device_type and self.power_port.device_type != self.device_type: if self.device_type and self.power_port.device_type != self.device_type:
raise ValidationError( 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: if self.module_type and self.power_port.module_type != self.module_type:
raise ValidationError( 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): def instantiate(self, **kwargs):
@ -359,15 +368,17 @@ class InterfaceTemplate(ModularComponentTemplateModel):
blank=True blank=True
) )
type = models.CharField( type = models.CharField(
verbose_name=_('type'),
max_length=50, max_length=50,
choices=InterfaceTypeChoices choices=InterfaceTypeChoices
) )
enabled = models.BooleanField( enabled = models.BooleanField(
verbose_name=_('enabled'),
default=True default=True
) )
mgmt_only = models.BooleanField( mgmt_only = models.BooleanField(
default=False, default=False,
verbose_name='Management only' verbose_name=_('Management only')
) )
bridge = models.ForeignKey( bridge = models.ForeignKey(
to='self', to='self',
@ -375,19 +386,19 @@ class InterfaceTemplate(ModularComponentTemplateModel):
related_name='bridge_interfaces', related_name='bridge_interfaces',
null=True, null=True,
blank=True, blank=True,
verbose_name='Bridge interface' verbose_name=_('Bridge interface')
) )
poe_mode = models.CharField( poe_mode = models.CharField(
max_length=50, max_length=50,
choices=InterfacePoEModeChoices, choices=InterfacePoEModeChoices,
blank=True, blank=True,
verbose_name='PoE mode' verbose_name=_('PoE mode')
) )
poe_type = models.CharField( poe_type = models.CharField(
max_length=50, max_length=50,
choices=InterfacePoETypeChoices, choices=InterfacePoETypeChoices,
blank=True, blank=True,
verbose_name='PoE type' verbose_name=_('PoE type')
) )
rf_role = models.CharField( rf_role = models.CharField(
max_length=30, max_length=30,
@ -403,14 +414,14 @@ class InterfaceTemplate(ModularComponentTemplateModel):
if self.bridge: if self.bridge:
if self.pk and self.bridge_id == self.pk: 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: if self.device_type and self.device_type != self.bridge.device_type:
raise ValidationError({ 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: if self.module_type and self.module_type != self.bridge.module_type:
raise ValidationError({ 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: 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. Template for a pass-through port on the front of a new Device.
""" """
type = models.CharField( type = models.CharField(
verbose_name=_('type'),
max_length=50, max_length=50,
choices=PortTypeChoices choices=PortTypeChoices
) )
color = ColorField( color = ColorField(
verbose_name=_('color'),
blank=True blank=True
) )
rear_port = models.ForeignKey( rear_port = models.ForeignKey(
@ -464,6 +477,7 @@ class FrontPortTemplate(ModularComponentTemplateModel):
related_name='frontport_templates' related_name='frontport_templates'
) )
rear_port_position = models.PositiveSmallIntegerField( rear_port_position = models.PositiveSmallIntegerField(
verbose_name=_('rear port position'),
default=1, default=1,
validators=[ validators=[
MinValueValidator(REARPORT_POSITIONS_MIN), MinValueValidator(REARPORT_POSITIONS_MIN),
@ -497,13 +511,13 @@ class FrontPortTemplate(ModularComponentTemplateModel):
# Validate rear port assignment # Validate rear port assignment
if self.rear_port.device_type != self.device_type: if self.rear_port.device_type != self.device_type:
raise ValidationError( 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 # Validate rear port position assignment
if self.rear_port_position > self.rear_port.positions: if self.rear_port_position > self.rear_port.positions:
raise ValidationError( 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 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. Template for a pass-through port on the rear of a new Device.
""" """
type = models.CharField( type = models.CharField(
verbose_name=_('type'),
max_length=50, max_length=50,
choices=PortTypeChoices choices=PortTypeChoices
) )
color = ColorField( color = ColorField(
verbose_name=_('color'),
blank=True blank=True
) )
positions = models.PositiveSmallIntegerField( positions = models.PositiveSmallIntegerField(
verbose_name=_('positions'),
default=1, default=1,
validators=[ validators=[
MinValueValidator(REARPORT_POSITIONS_MIN), MinValueValidator(REARPORT_POSITIONS_MIN),
@ -588,6 +605,7 @@ class ModuleBayTemplate(ComponentTemplateModel):
A template for a ModuleBay to be created for a new parent Device. A template for a ModuleBay to be created for a new parent Device.
""" """
position = models.CharField( position = models.CharField(
verbose_name=_('position'),
max_length=30, max_length=30,
blank=True, blank=True,
help_text=_('Identifier to reference when renaming installed components') help_text=_('Identifier to reference when renaming installed components')
@ -630,7 +648,7 @@ class DeviceBayTemplate(ComponentTemplateModel):
def clean(self): def clean(self):
if self.device_type and self.device_type.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT: if self.device_type and self.device_type.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT:
raise ValidationError( 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): def to_yaml(self):
@ -685,7 +703,7 @@ class InventoryItemTemplate(MPTTModel, ComponentTemplateModel):
) )
part_id = models.CharField( part_id = models.CharField(
max_length=50, max_length=50,
verbose_name='Part ID', verbose_name=_('Part ID'),
blank=True, blank=True,
help_text=_('Manufacturer-assigned part identifier') help_text=_('Manufacturer-assigned part identifier')
) )

View File

@ -2,6 +2,7 @@ from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.core.validators import ValidationError from django.core.validators import ValidationError
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel, TreeForeignKey from mptt.models import MPTTModel, TreeForeignKey
from netbox.models.features import * from netbox.models.features import *
@ -94,10 +95,12 @@ class PrimaryModel(NetBoxModel):
Primary models represent real objects within the infrastructure being modeled. Primary models represent real objects within the infrastructure being modeled.
""" """
description = models.CharField( description = models.CharField(
verbose_name=_('description'),
max_length=200, max_length=200,
blank=True blank=True
) )
comments = models.TextField( comments = models.TextField(
verbose_name=_('comments'),
blank=True blank=True
) )
@ -119,12 +122,15 @@ class NestedGroupModel(CloningMixin, NetBoxFeatureSet, MPTTModel):
db_index=True db_index=True
) )
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=100 max_length=100
) )
slug = models.SlugField( slug = models.SlugField(
verbose_name=_('slug'),
max_length=100 max_length=100
) )
description = models.CharField( description = models.CharField(
verbose_name=_('description'),
max_length=200, max_length=200,
blank=True blank=True
) )
@ -160,14 +166,17 @@ class OrganizationalModel(NetBoxFeatureSet, models.Model):
- Optional description - Optional description
""" """
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=100, max_length=100,
unique=True unique=True
) )
slug = models.SlugField( slug = models.SlugField(
verbose_name=_('slug'),
max_length=100, max_length=100,
unique=True unique=True
) )
description = models.CharField( description = models.CharField(
verbose_name=_('description'),
max_length=200, max_length=200,
blank=True blank=True
) )

View File

@ -9,7 +9,7 @@ from django.db import models
from django.db.models.signals import class_prepared from django.db.models.signals import class_prepared
from django.dispatch import receiver from django.dispatch import receiver
from django.utils import timezone 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 taggit.managers import TaggableManager
from core.choices import JobStatusChoices 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. Provides change logging support for a model. Adds the `created` and `last_updated` fields.
""" """
created = models.DateTimeField( created = models.DateTimeField(
verbose_name=_('created'),
auto_now_add=True, auto_now_add=True,
blank=True, blank=True,
null=True null=True
) )
last_updated = models.DateTimeField( last_updated = models.DateTimeField(
verbose_name=_('last updated'),
auto_now=True, auto_now=True,
blank=True, blank=True,
null=True null=True
@ -144,6 +146,7 @@ class CustomFieldsMixin(models.Model):
Enables support for custom fields. Enables support for custom fields.
""" """
custom_field_data = models.JSONField( custom_field_data = models.JSONField(
verbose_name=_('custom field data'),
encoder=CustomFieldJSONEncoder, encoder=CustomFieldJSONEncoder,
blank=True, blank=True,
default=dict default=dict
@ -401,16 +404,19 @@ class SyncedDataMixin(models.Model):
related_name='+' related_name='+'
) )
data_path = models.CharField( data_path = models.CharField(
verbose_name=_('data path'),
max_length=1000, max_length=1000,
blank=True, blank=True,
editable=False, editable=False,
help_text=_("Path to remote file (relative to data source root)") help_text=_("Path to remote file (relative to data source root)")
) )
auto_sync_enabled = models.BooleanField( auto_sync_enabled = models.BooleanField(
verbose_name=_('auto sync enabled'),
default=False, default=False,
help_text=_("Enable automatic synchronization of data when the data file is updated") help_text=_("Enable automatic synchronization of data when the data file is updated")
) )
data_synced = models.DateTimeField( data_synced = models.DateTimeField(
verbose_name=_('date synced'),
blank=True, blank=True,
null=True, null=True,
editable=False editable=False