13132 add gettext_lazy to models

This commit is contained in:
Arthur 2023-07-11 19:17:14 +07:00 committed by Jeremy Stretch
parent 90c9e71682
commit d051494cef
7 changed files with 83 additions and 45 deletions

View File

@ -1,3 +1,4 @@
from django.utils.translation import gettext_lazy as _
import django_tables2 as tables import django_tables2 as tables
from circuits.models import * from circuits.models import *
@ -53,19 +54,19 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
) )
provider_account = tables.Column( provider_account = tables.Column(
linkify=True, linkify=True,
verbose_name='Account' verbose_name=_('Account')
) )
status = columns.ChoiceFieldColumn() status = columns.ChoiceFieldColumn()
termination_a = tables.TemplateColumn( termination_a = tables.TemplateColumn(
template_code=CIRCUITTERMINATION_LINK, template_code=CIRCUITTERMINATION_LINK,
verbose_name='Side A' verbose_name=_('Side A')
) )
termination_z = tables.TemplateColumn( termination_z = tables.TemplateColumn(
template_code=CIRCUITTERMINATION_LINK, template_code=CIRCUITTERMINATION_LINK,
verbose_name='Side Z' verbose_name=_('Side Z')
) )
commit_rate = CommitRateColumn( commit_rate = CommitRateColumn(
verbose_name='Commit Rate' verbose_name=_('Commit Rate')
) )
comments = columns.MarkdownColumn() comments = columns.MarkdownColumn()
tags = columns.TagColumn( tags = columns.TagColumn(

View File

@ -2,6 +2,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
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_lazy as _
from netbox.models import ChangeLoggedModel, NestedGroupModel, OrganizationalModel, PrimaryModel from netbox.models import ChangeLoggedModel, NestedGroupModel, OrganizationalModel, PrimaryModel
from tenancy.choices import * from tenancy.choices import *
@ -51,24 +52,30 @@ class Contact(PrimaryModel):
null=True null=True
) )
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=100 max_length=100
) )
title = models.CharField( title = models.CharField(
verbose_name=_('title'),
max_length=100, max_length=100,
blank=True blank=True
) )
phone = models.CharField( phone = models.CharField(
verbose_name=_('phone'),
max_length=50, max_length=50,
blank=True blank=True
) )
email = models.EmailField( email = models.EmailField(
verbose_name=_('email'),
blank=True blank=True
) )
address = models.CharField( address = models.CharField(
verbose_name=_('address'),
max_length=200, max_length=200,
blank=True blank=True
) )
link = models.URLField( link = models.URLField(
verbose_name=_('link'),
blank=True blank=True
) )
@ -113,6 +120,7 @@ class ContactAssignment(ChangeLoggedModel):
related_name='assignments' related_name='assignments'
) )
priority = models.CharField( priority = models.CharField(
verbose_name=_('priority'),
max_length=50, max_length=50,
choices=ContactPriorityChoices, choices=ContactPriorityChoices,
blank=True blank=True

View File

@ -2,6 +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_lazy as _
from netbox.models import NestedGroupModel, PrimaryModel from netbox.models import NestedGroupModel, PrimaryModel
@ -16,10 +17,12 @@ class TenantGroup(NestedGroupModel):
An arbitrary collection of Tenants. An arbitrary collection of Tenants.
""" """
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
) )
@ -37,9 +40,11 @@ class Tenant(PrimaryModel):
department. department.
""" """
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
) )
group = models.ForeignKey( group = models.ForeignKey(
@ -65,7 +70,7 @@ class Tenant(PrimaryModel):
models.UniqueConstraint( models.UniqueConstraint(
fields=('group', 'name'), fields=('group', 'name'),
name='%(app_label)s_%(class)s_unique_group_name', name='%(app_label)s_%(class)s_unique_group_name',
violation_error_message="Tenant name must be unique per group." violation_error_message=_("Tenant name must be unique per group.")
), ),
models.UniqueConstraint( models.UniqueConstraint(
fields=('name',), fields=('name',),
@ -75,7 +80,7 @@ class Tenant(PrimaryModel):
models.UniqueConstraint( models.UniqueConstraint(
fields=('group', 'slug'), fields=('group', 'slug'),
name='%(app_label)s_%(class)s_unique_group_slug', name='%(app_label)s_%(class)s_unique_group_slug',
violation_error_message="Tenant slug must be unique per group." violation_error_message=_("Tenant slug must be unique per group.")
), ),
models.UniqueConstraint( models.UniqueConstraint(
fields=('slug',), fields=('slug',),

View File

@ -11,7 +11,7 @@ from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.urls import reverse from django.urls import reverse
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 netaddr import IPNetwork from netaddr import IPNetwork
from ipam.fields import IPNetworkField from ipam.fields import IPNetworkField
@ -40,7 +40,7 @@ class AdminGroup(Group):
Proxy contrib.auth.models.Group for the admin UI Proxy contrib.auth.models.Group for the admin UI
""" """
class Meta: class Meta:
verbose_name = 'Group' verbose_name = _('Group')
proxy = True proxy = True
@ -49,7 +49,7 @@ class AdminUser(User):
Proxy contrib.auth.models.User for the admin UI Proxy contrib.auth.models.User for the admin UI
""" """
class Meta: class Meta:
verbose_name = 'User' verbose_name = _('User')
proxy = True proxy = True
@ -105,12 +105,13 @@ class UserConfig(models.Model):
related_name='config' related_name='config'
) )
data = models.JSONField( data = models.JSONField(
verbose_name=_('data'),
default=dict default=dict
) )
class Meta: class Meta:
ordering = ['user'] ordering = ['user']
verbose_name = verbose_name_plural = 'User Preferences' verbose_name = verbose_name_plural = _('User Preferences')
def get(self, path, default=None): def get(self, path, default=None):
""" """
@ -176,7 +177,7 @@ class UserConfig(models.Model):
d = d[key] d = d[key]
elif key in d: elif key in d:
err_path = '.'.join(path.split('.')[:i + 1]) err_path = '.'.join(path.split('.')[:i + 1])
raise TypeError(f"Key '{err_path}' is a leaf node; cannot assign new keys") raise TypeError(_("Key '{err_path}' is a leaf node; cannot assign new keys").format(err_path=err_path))
else: else:
d = d.setdefault(key, {}) d = d.setdefault(key, {})
@ -186,7 +187,7 @@ class UserConfig(models.Model):
if type(value) is dict: if type(value) is dict:
d[key].update(value) d[key].update(value)
else: else:
raise TypeError(f"Key '{path}' is a dictionary; cannot assign a non-dictionary value") raise TypeError(_("Key '{path}' is a dictionary; cannot assign a non-dictionary value").format(path=path))
else: else:
d[key] = value d[key] = value
@ -246,26 +247,32 @@ class Token(models.Model):
related_name='tokens' related_name='tokens'
) )
created = models.DateTimeField( created = models.DateTimeField(
verbose_name=_('created'),
auto_now_add=True auto_now_add=True
) )
expires = models.DateTimeField( expires = models.DateTimeField(
verbose_name=_('expires'),
blank=True, blank=True,
null=True null=True
) )
last_used = models.DateTimeField( last_used = models.DateTimeField(
verbose_name=_('last used'),
blank=True, blank=True,
null=True null=True
) )
key = models.CharField( key = models.CharField(
verbose_name=_('key'),
max_length=40, max_length=40,
unique=True, unique=True,
validators=[MinLengthValidator(40)] validators=[MinLengthValidator(40)]
) )
write_enabled = models.BooleanField( write_enabled = models.BooleanField(
verbose_name=_('write enabled'),
default=True, default=True,
help_text=_('Permit create/update/delete operations using this key') help_text=_('Permit create/update/delete operations using this key')
) )
description = models.CharField( description = models.CharField(
verbose_name=_('description'),
max_length=200, max_length=200,
blank=True blank=True
) )
@ -273,7 +280,7 @@ class Token(models.Model):
base_field=IPNetworkField(), base_field=IPNetworkField(),
blank=True, blank=True,
null=True, null=True,
verbose_name='Allowed IPs', verbose_name=_('Allowed IPs'),
help_text=_( help_text=_(
'Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for no restrictions. ' '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"' 'Ex: "10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64"'
@ -344,13 +351,16 @@ class ObjectPermission(models.Model):
identified by ORM query parameters. identified by ORM query parameters.
""" """
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
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
) )
enabled = models.BooleanField( enabled = models.BooleanField(
verbose_name=_('enabled'),
default=True default=True
) )
object_types = models.ManyToManyField( object_types = models.ManyToManyField(
@ -382,7 +392,7 @@ class ObjectPermission(models.Model):
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
verbose_name = "permission" verbose_name = _("permission")
def __str__(self): def __str__(self):
return self.name return self.name

View File

@ -2,6 +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_lazy as _
from dcim.models import Device from dcim.models import Device
from netbox.models import OrganizationalModel, PrimaryModel from netbox.models import OrganizationalModel, PrimaryModel
@ -46,9 +47,11 @@ class Cluster(PrimaryModel):
A cluster of VirtualMachines. Each Cluster may optionally be associated with one or more Devices. A cluster of VirtualMachines. Each Cluster may optionally be associated with one or more Devices.
""" """
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=100 max_length=100
) )
type = models.ForeignKey( type = models.ForeignKey(
verbose_name=_('type'),
to=ClusterType, to=ClusterType,
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='clusters' related_name='clusters'
@ -61,6 +64,7 @@ class Cluster(PrimaryModel):
null=True null=True
) )
status = models.CharField( status = models.CharField(
verbose_name=_('status'),
max_length=50, max_length=50,
choices=ClusterStatusChoices, choices=ClusterStatusChoices,
default=ClusterStatusChoices.STATUS_ACTIVE default=ClusterStatusChoices.STATUS_ACTIVE
@ -128,7 +132,7 @@ class Cluster(PrimaryModel):
nonsite_devices = Device.objects.filter(cluster=self).exclude(site=self.site).count() nonsite_devices = Device.objects.filter(cluster=self).exclude(site=self.site).count()
if nonsite_devices: if nonsite_devices:
raise ValidationError({ raise ValidationError({
'site': "{} devices are assigned as hosts for this cluster but are not in site {}".format( 'site': _("{} devices are assigned as hosts for this cluster but are not in site {}").format(
nonsite_devices, self.site nonsite_devices, self.site
) )
}) })

View File

@ -5,6 +5,7 @@ from django.db import models
from django.db.models import Q from django.db.models import Q
from django.db.models.functions import Lower from django.db.models.functions import Lower
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from dcim.models import BaseInterface from dcim.models import BaseInterface
from extras.models import ConfigContextModel from extras.models import ConfigContextModel
@ -63,6 +64,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
null=True null=True
) )
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=64 max_length=64
) )
_name = NaturalOrderingField( _name = NaturalOrderingField(
@ -74,7 +76,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
max_length=50, max_length=50,
choices=VirtualMachineStatusChoices, choices=VirtualMachineStatusChoices,
default=VirtualMachineStatusChoices.STATUS_ACTIVE, default=VirtualMachineStatusChoices.STATUS_ACTIVE,
verbose_name='Status' verbose_name=_('Status')
) )
role = models.ForeignKey( role = models.ForeignKey(
to='dcim.DeviceRole', to='dcim.DeviceRole',
@ -113,12 +115,12 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
memory = models.PositiveIntegerField( memory = models.PositiveIntegerField(
blank=True, blank=True,
null=True, null=True,
verbose_name='Memory (MB)' verbose_name=_('Memory (MB)')
) )
disk = models.PositiveIntegerField( disk = models.PositiveIntegerField(
blank=True, blank=True,
null=True, null=True,
verbose_name='Disk (GB)' verbose_name=_('Disk (GB)')
) )
# Counter fields # Counter fields
@ -152,7 +154,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
Lower('name'), 'cluster', Lower('name'), 'cluster',
name='%(app_label)s_%(class)s_unique_name_cluster', name='%(app_label)s_%(class)s_unique_name_cluster',
condition=Q(tenant__isnull=True), condition=Q(tenant__isnull=True),
violation_error_message="Virtual machine name must be unique per cluster." violation_error_message=_("Virtual machine name must be unique per cluster.")
), ),
) )
@ -168,23 +170,23 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
# Must be assigned to a site and/or cluster # Must be assigned to a site and/or cluster
if not self.site and not self.cluster: if not self.site and not self.cluster:
raise ValidationError({ raise ValidationError({
'cluster': f'A virtual machine must be assigned to a site and/or cluster.' 'cluster': _('A virtual machine must be assigned to a site and/or cluster.')
}) })
# Validate site for cluster & device # Validate site for cluster & device
if self.cluster and self.site and self.cluster.site != self.site: if self.cluster and self.site and self.cluster.site != self.site:
raise ValidationError({ raise ValidationError({
'cluster': f'The selected cluster ({self.cluster}) is not assigned to this 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 # Validate assigned cluster device
if self.device and not self.cluster: if self.device and not self.cluster:
raise ValidationError({ raise ValidationError({
'device': f'Must specify a cluster when assigning a host device.' 'device': _('Must specify a cluster when assigning a host device.')
}) })
if self.device and self.device not in self.cluster.devices.all(): if self.device and self.device not in self.cluster.devices.all():
raise ValidationError({ raise ValidationError({
'device': f'The selected device ({self.device}) is not assigned to this 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 # Validate primary IP addresses
@ -195,7 +197,8 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
if ip is not None: if ip is not None:
if ip.address.version != family: if ip.address.version != family:
raise ValidationError({ raise ValidationError({
field: f"Must be an IPv{family} address. ({ip} is an IPv{ip.address.version} address.)", 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: if ip.assigned_object in interfaces:
pass pass
@ -203,7 +206,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
pass pass
else: else:
raise ValidationError({ raise ValidationError({
field: f"The specified IP address ({ip}) is not assigned to this VM.", field: _("The specified IP address ({ip}) is not assigned to this VM.").format(ip=ip),
}) })
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
@ -236,6 +239,7 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin):
related_name='interfaces' related_name='interfaces'
) )
name = models.CharField( name = models.CharField(
verbose_name=_('name'),
max_length=64 max_length=64
) )
_name = NaturalOrderingField( _name = NaturalOrderingField(
@ -245,6 +249,7 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin):
blank=True blank=True
) )
description = models.CharField( description = models.CharField(
verbose_name=_('description'),
max_length=200, max_length=200,
blank=True blank=True
) )
@ -254,13 +259,13 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin):
related_name='vminterfaces_as_untagged', related_name='vminterfaces_as_untagged',
null=True, null=True,
blank=True, blank=True,
verbose_name='Untagged VLAN' verbose_name=_('Untagged VLAN')
) )
tagged_vlans = models.ManyToManyField( tagged_vlans = models.ManyToManyField(
to='ipam.VLAN', to='ipam.VLAN',
related_name='vminterfaces_as_tagged', related_name='vminterfaces_as_tagged',
blank=True, blank=True,
verbose_name='Tagged VLANs' verbose_name=_('Tagged VLANs')
) )
ip_addresses = GenericRelation( ip_addresses = GenericRelation(
to='ipam.IPAddress', to='ipam.IPAddress',
@ -274,7 +279,7 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin):
related_name='vminterfaces', related_name='vminterfaces',
null=True, null=True,
blank=True, blank=True,
verbose_name='VRF' verbose_name=_('VRF')
) )
fhrp_group_assignments = GenericRelation( fhrp_group_assignments = GenericRelation(
to='ipam.FHRPGroupAssignment', to='ipam.FHRPGroupAssignment',
@ -312,26 +317,26 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin):
# An interface cannot be its own parent # An interface cannot be its own parent
if self.pk and self.parent_id == self.pk: if self.pk and self.parent_id == self.pk:
raise ValidationError({'parent': "An interface cannot be its own parent."}) raise ValidationError({'parent': _("An interface cannot be its own parent.")})
# An interface's parent must belong to the same virtual machine # An interface's parent must belong to the same virtual machine
if self.parent and self.parent.virtual_machine != self.virtual_machine: if self.parent and self.parent.virtual_machine != self.virtual_machine:
raise ValidationError({ raise ValidationError({
'parent': f"The selected parent interface ({self.parent}) belongs to a different virtual machine " 'parent': _("The selected parent interface ({parent}) belongs to a different virtual machine "
f"({self.parent.virtual_machine})." "({virtual_machine}).").format(parent=self.parent, virtual_machine=self.parent.virtual_machine)
}) })
# Bridge validation # Bridge validation
# An interface cannot be bridged to itself # An interface cannot be bridged to itself
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.")})
# A bridged interface belong to the same virtual machine # A bridged interface belong to the same virtual machine
if self.bridge and self.bridge.virtual_machine != self.virtual_machine: if self.bridge and self.bridge.virtual_machine != self.virtual_machine:
raise ValidationError({ raise ValidationError({
'bridge': f"The selected bridge interface ({self.bridge}) belongs to a different virtual machine " 'bridge': _("The selected bridge interface ({bridge}) belongs to a different virtual machine "
f"({self.bridge.virtual_machine})." "({virtual_machine}).").format(bridge=self.bridge, virtual_machine=self.bridge.virtual_machine)
}) })
# VLAN validation # VLAN validation
@ -339,8 +344,8 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin):
# Validate untagged VLAN # Validate untagged VLAN
if self.untagged_vlan and self.untagged_vlan.site not in [self.virtual_machine.site, None]: if self.untagged_vlan and self.untagged_vlan.site not in [self.virtual_machine.site, None]:
raise ValidationError({ raise ValidationError({
'untagged_vlan': f"The untagged VLAN ({self.untagged_vlan}) must belong to the same site as the " 'untagged_vlan': _("The untagged VLAN ({untagged_vlan}) must belong to the same site as the "
f"interface's parent virtual machine, or it must be global." "interface's parent virtual machine, or it must be global.").format(untagged_vlan=self.untagged_vlan)
}) })
def to_objectchange(self, action): def to_objectchange(self, action):

View File

@ -1,6 +1,7 @@
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_lazy as _
from mptt.models import MPTTModel from mptt.models import MPTTModel
from dcim.choices import LinkStatusChoices from dcim.choices import LinkStatusChoices
@ -24,9 +25,10 @@ class WirelessAuthenticationBase(models.Model):
max_length=50, max_length=50,
choices=WirelessAuthTypeChoices, choices=WirelessAuthTypeChoices,
blank=True, blank=True,
verbose_name="Auth Type", verbose_name=_("Auth Type"),
) )
auth_cipher = models.CharField( auth_cipher = models.CharField(
verbose_name=_('auth cipher'),
max_length=50, max_length=50,
choices=WirelessAuthCipherChoices, choices=WirelessAuthCipherChoices,
blank=True blank=True
@ -34,7 +36,7 @@ class WirelessAuthenticationBase(models.Model):
auth_psk = models.CharField( auth_psk = models.CharField(
max_length=PSK_MAX_LENGTH, max_length=PSK_MAX_LENGTH,
blank=True, blank=True,
verbose_name='Pre-shared key' verbose_name=_('Pre-shared key')
) )
class Meta: class Meta:
@ -46,10 +48,12 @@ class WirelessLANGroup(NestedGroupModel):
A nested grouping of WirelessLANs A nested grouping of WirelessLANs
""" """
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
) )
@ -74,7 +78,7 @@ class WirelessLAN(WirelessAuthenticationBase, PrimaryModel):
""" """
ssid = models.CharField( ssid = models.CharField(
max_length=SSID_MAX_LENGTH, max_length=SSID_MAX_LENGTH,
verbose_name='SSID' verbose_name=_('SSID')
) )
group = models.ForeignKey( group = models.ForeignKey(
to='wireless.WirelessLANGroup', to='wireless.WirelessLANGroup',
@ -93,7 +97,7 @@ class WirelessLAN(WirelessAuthenticationBase, PrimaryModel):
on_delete=models.PROTECT, on_delete=models.PROTECT,
blank=True, blank=True,
null=True, null=True,
verbose_name='VLAN' verbose_name=_('VLAN')
) )
tenant = models.ForeignKey( tenant = models.ForeignKey(
to='tenancy.Tenant', to='tenancy.Tenant',
@ -134,21 +138,22 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
limit_choices_to=get_wireless_interface_types, limit_choices_to=get_wireless_interface_types,
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='+', related_name='+',
verbose_name="Interface A", verbose_name=_('Interface A'),
) )
interface_b = models.ForeignKey( interface_b = models.ForeignKey(
to='dcim.Interface', to='dcim.Interface',
limit_choices_to=get_wireless_interface_types, limit_choices_to=get_wireless_interface_types,
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='+', related_name='+',
verbose_name="Interface B", verbose_name=_('Interface B'),
) )
ssid = models.CharField( ssid = models.CharField(
max_length=SSID_MAX_LENGTH, max_length=SSID_MAX_LENGTH,
blank=True, blank=True,
verbose_name='SSID' verbose_name=_('SSID')
) )
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
@ -203,11 +208,11 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
# Validate interface types # Validate interface types
if self.interface_a.type not in WIRELESS_IFACE_TYPES: if self.interface_a.type not in WIRELESS_IFACE_TYPES:
raise ValidationError({ raise ValidationError({
'interface_a': f"{self.interface_a.get_type_display()} is not a wireless interface." '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: if self.interface_b.type not in WIRELESS_IFACE_TYPES:
raise ValidationError({ raise ValidationError({
'interface_a': f"{self.interface_b.get_type_display()} is not a wireless interface." 'interface_a': _("{type_display} is not a wireless interface.").format(type_display=self.interface_b.get_type_display())
}) })
def save(self, *args, **kwargs): def save(self, *args, **kwargs):