Skip to content

Commit 4246da9

Browse files
committed
feat(models): Add internationalization support
Introduces internationalization (i18n) to AccessList models. Updates field definitions to use `gettext_lazy` for translations and improves model class field consistency. Includes minor corrections to docstrings and comments.
1 parent 688f1fc commit 4246da9

File tree

2 files changed

+96
-94
lines changed

2 files changed

+96
-94
lines changed

netbox_acls/models/access_list_rules.py

Lines changed: 64 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
Define the django models for this plugin.
33
"""
44

5-
from django.apps import apps
65
from django.contrib.postgres.fields import ArrayField
76
from django.db import models
87
from django.urls import reverse
8+
from django.utils.translation import gettext_lazy as _
99
from netbox.models import NetBoxModel
1010

1111
from ..choices import ACLProtocolChoices, ACLRuleActionChoices, ACLTypeChoices
@@ -21,48 +21,42 @@
2121
class ACLRule(NetBoxModel):
2222
"""
2323
Abstract model for ACL Rules.
24-
Inherrited by both ACLStandardRule and ACLExtendedRule.
24+
Inherited by both ACLStandardRule and ACLExtendedRule.
2525
"""
2626

2727
access_list = models.ForeignKey(
28-
on_delete=models.CASCADE,
2928
to=AccessList,
30-
verbose_name="Access List",
29+
on_delete=models.CASCADE,
3130
related_name="rules",
31+
verbose_name=_("Access List"),
3232
)
3333
index = models.PositiveIntegerField()
3434
remark = models.CharField(
35+
verbose_name=_("Remark"),
3536
max_length=500,
3637
blank=True,
3738
)
3839
description = models.CharField(
40+
verbose_name=_("Description"),
3941
max_length=500,
4042
blank=True,
4143
)
4244
action = models.CharField(
43-
choices=ACLRuleActionChoices,
45+
verbose_name=_("Action"),
4446
max_length=30,
47+
choices=ACLRuleActionChoices,
4548
)
4649
source_prefix = models.ForeignKey(
47-
blank=True,
48-
null=True,
50+
to="ipam.prefix",
4951
on_delete=models.PROTECT,
5052
related_name="+",
51-
to="ipam.Prefix",
52-
verbose_name="Source Prefix",
53+
verbose_name=_("Source Prefix"),
54+
blank=True,
55+
null=True,
5356
)
5457

5558
clone_fields = ("access_list", "action", "source_prefix")
56-
57-
def __str__(self):
58-
return f"{self.access_list}: Rule {self.index}"
59-
60-
def get_action_color(self):
61-
return ACLRuleActionChoices.colors.get(self.action)
62-
63-
@classmethod
64-
def get_prerequisite_models(cls):
65-
return [apps.get_model("ipam.Prefix"), AccessList]
59+
prerequisite_models = ("netbox_acls.AccessList",)
6660

6761
class Meta:
6862
"""
@@ -73,8 +67,24 @@ class Meta:
7367
"""
7468

7569
abstract = True
76-
ordering = ["access_list", "index"]
77-
unique_together = ["access_list", "index"]
70+
ordering = ("access_list", "index")
71+
unique_together = ("access_list", "index")
72+
73+
def __str__(self):
74+
return f"{self.access_list}: Rule {self.index}"
75+
76+
def get_absolute_url(self):
77+
"""
78+
The method is a Django convention; although not strictly required,
79+
it conveniently returns the absolute URL for any particular object.
80+
"""
81+
return reverse(
82+
f"plugins:{self._meta.app_label}:{self._meta.model_name}",
83+
args=[self.pk],
84+
)
85+
86+
def get_action_color(self):
87+
return ACLRuleActionChoices.colors.get(self.action)
7888

7989

8090
class ACLStandardRule(ACLRule):
@@ -83,24 +93,13 @@ class ACLStandardRule(ACLRule):
8393
"""
8494

8595
access_list = models.ForeignKey(
86-
on_delete=models.CASCADE,
8796
to=AccessList,
88-
verbose_name="Standard Access List",
89-
limit_choices_to={"type": ACLTypeChoices.TYPE_STANDARD},
97+
on_delete=models.CASCADE,
9098
related_name="aclstandardrules",
99+
limit_choices_to={"type": ACLTypeChoices.TYPE_STANDARD},
100+
verbose_name=_("Standard Access List"),
91101
)
92102

93-
def get_absolute_url(self):
94-
"""
95-
The method is a Django convention; although not strictly required,
96-
it conveniently returns the absolute URL for any particular object.
97-
"""
98-
return reverse("plugins:netbox_acls:aclstandardrule", args=[self.pk])
99-
100-
@classmethod
101-
def get_prerequisite_models(cls):
102-
return [AccessList]
103-
104103
class Meta(ACLRule.Meta):
105104
"""
106105
Define the model properties adding to or overriding the inherited class:
@@ -109,62 +108,60 @@ class Meta(ACLRule.Meta):
109108
- verbose name plural (for displaying in the GUI)
110109
"""
111110

112-
verbose_name = "ACL Standard Rule"
113-
verbose_name_plural = "ACL Standard Rules"
111+
verbose_name = _("ACL Standard Rule")
112+
verbose_name_plural = _("ACL Standard Rules")
114113

115114

116115
class ACLExtendedRule(ACLRule):
117116
"""
118117
Inherits ACLRule.
119-
Add ACLExtendedRule specific fields: source_ports, desintation_prefix, destination_ports, and protocol
118+
Add ACLExtendedRule specific fields: source_ports, destination_prefix, destination_ports, and protocol
120119
"""
121120

122121
access_list = models.ForeignKey(
123-
on_delete=models.CASCADE,
124122
to=AccessList,
125-
verbose_name="Extended Access List",
126-
limit_choices_to={"type": "extended"},
123+
on_delete=models.CASCADE,
127124
related_name="aclextendedrules",
125+
limit_choices_to={"type": "extended"},
126+
verbose_name=_("Extended Access List"),
128127
)
129128
source_ports = ArrayField(
130129
base_field=models.PositiveIntegerField(),
130+
verbose_name=_("Source Ports"),
131131
blank=True,
132132
null=True,
133-
verbose_name="Soure Ports",
134133
)
135134
destination_prefix = models.ForeignKey(
136-
blank=True,
137-
null=True,
135+
to="ipam.prefix",
138136
on_delete=models.PROTECT,
139137
related_name="+",
140-
to="ipam.Prefix",
141-
verbose_name="Destination Prefix",
138+
verbose_name=_("Destination Prefix"),
139+
blank=True,
140+
null=True,
142141
)
143142
destination_ports = ArrayField(
144143
base_field=models.PositiveIntegerField(),
144+
verbose_name=_("Destination Ports"),
145145
blank=True,
146146
null=True,
147-
verbose_name="Destination Ports",
148147
)
149148
protocol = models.CharField(
150-
blank=True,
151-
choices=ACLProtocolChoices,
149+
verbose_name=_("Protocol"),
152150
max_length=30,
151+
choices=ACLProtocolChoices,
152+
blank=True,
153153
)
154154

155-
def get_absolute_url(self):
156-
"""
157-
The method is a Django convention; although not strictly required,
158-
it conveniently returns the absolute URL for any particular object.
159-
"""
160-
return reverse("plugins:netbox_acls:aclextendedrule", args=[self.pk])
161-
162-
def get_protocol_color(self):
163-
return ACLProtocolChoices.colors.get(self.protocol)
164-
165-
@classmethod
166-
def get_prerequisite_models(cls):
167-
return [apps.get_model("ipam.Prefix"), AccessList]
155+
clone_fields = (
156+
"access_list",
157+
"action",
158+
"source_prefix",
159+
"source_ports",
160+
"destination_prefix",
161+
"destination_ports",
162+
"protocol",
163+
)
164+
prerequisite_models = ("netbox_acls.AccessList",)
168165

169166
class Meta(ACLRule.Meta):
170167
"""
@@ -174,5 +171,8 @@ class Meta(ACLRule.Meta):
174171
- verbose name plural (for displaying in the GUI)
175172
"""
176173

177-
verbose_name = "ACL Extended Rule"
178-
verbose_name_plural = "ACL Extended Rules"
174+
verbose_name = _("ACL Extended Rule")
175+
verbose_name_plural = _("ACL Extended Rules")
176+
177+
def get_protocol_color(self):
178+
return ACLProtocolChoices.colors.get(self.protocol)

netbox_acls/models/access_lists.py

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
from dcim.models import Device, Interface, VirtualChassis
66
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
7-
from django.contrib.contenttypes.models import ContentType
87
from django.core.exceptions import ValidationError
98
from django.core.validators import RegexValidator
109
from django.db import models
1110
from django.urls import reverse
11+
from django.utils.translation import gettext_lazy as _
1212
from netbox.models import NetBoxModel
1313
from virtualization.models import VirtualMachine, VMInterface
1414

@@ -23,53 +23,57 @@
2323

2424
alphanumeric_plus = RegexValidator(
2525
r"^[a-zA-Z0-9-_]+$",
26-
"Only alphanumeric, hyphens, and underscores characters are allowed.",
26+
_("Only alphanumeric, hyphens, and underscores characters are allowed."),
2727
)
2828

2929

3030
class AccessList(NetBoxModel):
3131
"""
32-
Model defintion for Access Lists.
32+
Model definition for Access Lists.
3333
"""
3434

3535
name = models.CharField(
36+
verbose_name=_("Name"),
3637
max_length=500,
3738
validators=[alphanumeric_plus],
3839
)
3940
assigned_object_type = models.ForeignKey(
40-
to=ContentType,
41-
limit_choices_to=ACL_HOST_ASSIGNMENT_MODELS,
41+
to="contenttypes.ContentType",
4242
on_delete=models.PROTECT,
43+
related_name="+",
44+
limit_choices_to=ACL_HOST_ASSIGNMENT_MODELS,
45+
verbose_name=_("Assigned Object Type"),
4346
)
4447
assigned_object_id = models.PositiveBigIntegerField()
4548
assigned_object = GenericForeignKey(
4649
ct_field="assigned_object_type",
4750
fk_field="assigned_object_id",
4851
)
4952
type = models.CharField(
53+
verbose_name=_("Type"),
5054
max_length=30,
5155
choices=ACLTypeChoices,
5256
)
5357
default_action = models.CharField(
54-
default=ACLActionChoices.ACTION_DENY,
58+
verbose_name=_("Default Action"),
5559
max_length=30,
60+
default=ACLActionChoices.ACTION_DENY,
5661
choices=ACLActionChoices,
57-
verbose_name="Default Action",
5862
)
5963
comments = models.TextField(
6064
blank=True,
6165
)
6266

6367
clone_fields = (
64-
"type",
6568
"default_action",
69+
"type",
6670
)
6771

6872
class Meta:
69-
unique_together = ["assigned_object_type", "assigned_object_id", "name"]
70-
ordering = ["assigned_object_type", "assigned_object_id", "name"]
71-
verbose_name = "Access List"
72-
verbose_name_plural = "Access Lists"
73+
unique_together = ("assigned_object_type", "assigned_object_id", "name")
74+
ordering = ("assigned_object_type", "assigned_object_id", "name")
75+
verbose_name = _("Access List")
76+
verbose_name_plural = _("Access Lists")
7377

7478
def __str__(self):
7579
return self.name
@@ -90,25 +94,28 @@ def get_type_color(self):
9094

9195
class ACLInterfaceAssignment(NetBoxModel):
9296
"""
93-
Model defintion for Access Lists associations with other Host interfaces:
97+
Model definition for Access Lists associations with other Host interfaces:
9498
- VM interfaces
9599
- device interface
96100
- tbd on more
97101
"""
98102

99103
access_list = models.ForeignKey(
100-
on_delete=models.CASCADE,
101104
to=AccessList,
102-
verbose_name="Access List",
105+
on_delete=models.CASCADE,
106+
verbose_name=_("Access List"),
103107
)
104108
direction = models.CharField(
109+
verbose_name=_("Direction"),
105110
max_length=30,
106111
choices=ACLAssignmentDirectionChoices,
107112
)
108113
assigned_object_type = models.ForeignKey(
109-
to=ContentType,
110-
limit_choices_to=ACL_INTERFACE_ASSIGNMENT_MODELS,
114+
to="contenttypes.ContentType",
111115
on_delete=models.PROTECT,
116+
related_name="+",
117+
limit_choices_to=ACL_INTERFACE_ASSIGNMENT_MODELS,
118+
verbose_name=_("Assigned Object Type"),
112119
)
113120
assigned_object_id = models.PositiveBigIntegerField()
114121
assigned_object = GenericForeignKey(
@@ -120,22 +127,23 @@ class ACLInterfaceAssignment(NetBoxModel):
120127
)
121128

122129
clone_fields = ("access_list", "direction")
130+
prerequisite_models = ("netbox_acls.AccessList",)
123131

124132
class Meta:
125-
unique_together = [
133+
unique_together = (
126134
"assigned_object_type",
127135
"assigned_object_id",
128136
"access_list",
129137
"direction",
130-
]
131-
ordering = [
138+
)
139+
ordering = (
132140
"assigned_object_type",
133141
"assigned_object_id",
134142
"access_list",
135143
"direction",
136-
]
137-
verbose_name = "ACL Interface Assignment"
138-
verbose_name_plural = "ACL Interface Assignments"
144+
)
145+
verbose_name = _("ACL Interface Assignment")
146+
verbose_name_plural = _("ACL Interface Assignments")
139147

140148
def get_absolute_url(self):
141149
"""
@@ -158,15 +166,9 @@ def clean(self):
158166
# Check if the assigned interface's host is the same as the host assigned to the access list.
159167
if interface_host != self.access_list.assigned_object:
160168
raise ValidationError(
161-
{
162-
"assigned_object": "The assigned object must be the same as the device assigned to it."
163-
}
169+
{"assigned_object": _("The assigned object must be the same as the device assigned to it.")}
164170
)
165171

166-
@classmethod
167-
def get_prerequisite_models(cls):
168-
return [AccessList]
169-
170172
def get_direction_color(self):
171173
return ACLAssignmentDirectionChoices.colors.get(self.direction)
172174

0 commit comments

Comments
 (0)