Skip to content

Commit 6b7d23d

Browse files
authored
Closes #17841 Allows Tags to be displayed in specified order (#18930)
1 parent d25605c commit 6b7d23d

File tree

15 files changed

+110
-18
lines changed

15 files changed

+110
-18
lines changed

docs/models/extras/tag.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ A unique URL-friendly identifier. (This value will be used for filtering.) This
1616

1717
The color to use when displaying the tag in the NetBox UI.
1818

19+
### Weight
20+
21+
A numeric weight employed to influence the ordering of tags. Tags with a lower weight will be listed before those with higher weights. Values must be within the range **0** to **32767**.
22+
23+
!!! info "This field was introduced in NetBox v4.3."
24+
1925
### Object Types
2026

2127
The assignment of a tag may be limited to a prescribed set of objects. For example, it may be desirable to limit the application of a specific tag to only devices and virtual machines.

netbox/extras/api/serializers_/tags.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ class TagSerializer(ValidatedModelSerializer):
2727
class Meta:
2828
model = Tag
2929
fields = [
30-
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'object_types',
31-
'tagged_items', 'created', 'last_updated',
30+
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'weight',
31+
'object_types', 'tagged_items', 'created', 'last_updated',
3232
]
3333
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'color', 'description')
3434

netbox/extras/filtersets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ class TagFilterSet(ChangeLoggedModelFilterSet):
450450

451451
class Meta:
452452
model = Tag
453-
fields = ('id', 'name', 'slug', 'color', 'description', 'object_types')
453+
fields = ('id', 'name', 'slug', 'color', 'weight', 'description', 'object_types')
454454

455455
def search(self, queryset, name, value):
456456
if not value.strip():

netbox/extras/forms/bulk_edit.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ class TagBulkEditForm(BulkEditForm):
275275
max_length=200,
276276
required=False
277277
)
278+
weight = forms.IntegerField(
279+
label=_('Weight'),
280+
required=False
281+
)
278282

279283
nullable_fields = ('description',)
280284

netbox/extras/forms/bulk_import.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,14 @@ def clean(self):
232232

233233
class TagImportForm(CSVModelForm):
234234
slug = SlugField()
235+
weight = forms.IntegerField(
236+
label=_('Weight'),
237+
required=False
238+
)
235239

236240
class Meta:
237241
model = Tag
238-
fields = ('name', 'slug', 'color', 'description')
242+
fields = ('name', 'slug', 'color', 'weight', 'description')
239243

240244

241245
class JournalEntryImportForm(NetBoxModelImportForm):

netbox/extras/forms/model_forms.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,15 +490,19 @@ class TagForm(forms.ModelForm):
490490
queryset=ObjectType.objects.with_feature('tags'),
491491
required=False
492492
)
493+
weight = forms.IntegerField(
494+
label=_('Weight'),
495+
required=False
496+
)
493497

494498
fieldsets = (
495-
FieldSet('name', 'slug', 'color', 'description', 'object_types', name=_('Tag')),
499+
FieldSet('name', 'slug', 'color', 'weight', 'description', 'object_types', name=_('Tag')),
496500
)
497501

498502
class Meta:
499503
model = Tag
500504
fields = [
501-
'name', 'slug', 'color', 'description', 'object_types',
505+
'name', 'slug', 'color', 'weight', 'description', 'object_types',
502506
]
503507

504508

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Generated by Django 5.2b1 on 2025-03-17 14:41
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('extras', '0123_remove_staging'),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name='tag',
15+
options={'ordering': ('weight', 'name')},
16+
),
17+
migrations.AddField(
18+
model_name='tag',
19+
name='weight',
20+
field=models.PositiveSmallIntegerField(default=0),
21+
),
22+
]

netbox/extras/models/tags.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,17 @@ class Tag(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, TagBase):
4040
blank=True,
4141
help_text=_("The object type(s) to which this tag can be applied.")
4242
)
43+
weight = models.PositiveSmallIntegerField(
44+
verbose_name=_('weight'),
45+
default=0,
46+
)
4347

4448
clone_fields = (
4549
'color', 'description', 'object_types',
4650
)
4751

4852
class Meta:
49-
ordering = ['name']
53+
ordering = ('weight', 'name')
5054
verbose_name = _('tag')
5155
verbose_name_plural = _('tags')
5256

netbox/extras/tables/tables.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,8 @@ class TagTable(NetBoxTable):
449449
class Meta(NetBoxTable.Meta):
450450
model = Tag
451451
fields = (
452-
'pk', 'id', 'name', 'items', 'slug', 'color', 'description', 'object_types', 'created', 'last_updated',
453-
'actions',
452+
'pk', 'id', 'name', 'items', 'slug', 'color', 'weight', 'description', 'object_types',
453+
'created', 'last_updated', 'actions',
454454
)
455455
default_columns = ('pk', 'name', 'items', 'slug', 'color', 'description')
456456

netbox/extras/tests/test_api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ class TagTest(APIViewTestCases.APIViewTestCase):
513513
{
514514
'name': 'Tag 4',
515515
'slug': 'tag-4',
516+
'weight': 1000,
516517
},
517518
{
518519
'name': 'Tag 5',
@@ -533,7 +534,7 @@ def setUpTestData(cls):
533534
tags = (
534535
Tag(name='Tag 1', slug='tag-1'),
535536
Tag(name='Tag 2', slug='tag-2'),
536-
Tag(name='Tag 3', slug='tag-3'),
537+
Tag(name='Tag 3', slug='tag-3', weight=26),
537538
)
538539
Tag.objects.bulk_create(tags)
539540

netbox/extras/tests/test_filtersets.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1196,7 +1196,7 @@ def setUpTestData(cls):
11961196
tags = (
11971197
Tag(name='Tag 1', slug='tag-1', color='ff0000', description='foobar1'),
11981198
Tag(name='Tag 2', slug='tag-2', color='00ff00', description='foobar2'),
1199-
Tag(name='Tag 3', slug='tag-3', color='0000ff'),
1199+
Tag(name='Tag 3', slug='tag-3', color='0000ff', weight=1000),
12001200
)
12011201
Tag.objects.bulk_create(tags)
12021202
tags[0].object_types.add(object_types['site'])
@@ -1249,6 +1249,13 @@ def test_object_types(self):
12491249
['Tag 2', 'Tag 3']
12501250
)
12511251

1252+
def test_weight(self):
1253+
params = {'weight': [1000]}
1254+
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
1255+
1256+
params = {'weight': [0]}
1257+
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
1258+
12521259

12531260
class TaggedItemFilterSetTestCase(TestCase):
12541261
queryset = TaggedItem.objects.all()

netbox/extras/tests/test_models.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,40 @@
1010

1111
class TagTest(TestCase):
1212

13+
def test_default_ordering_weight_then_name_is_set(self):
14+
Tag.objects.create(name='Tag 1', slug='tag-1', weight=100)
15+
Tag.objects.create(name='Tag 2', slug='tag-2')
16+
Tag.objects.create(name='Tag 3', slug='tag-3', weight=10)
17+
Tag.objects.create(name='Tag 4', slug='tag-4', weight=10)
18+
19+
tags = Tag.objects.all()
20+
21+
self.assertEqual(tags[0].slug, 'tag-2')
22+
self.assertEqual(tags[1].slug, 'tag-3')
23+
self.assertEqual(tags[2].slug, 'tag-4')
24+
self.assertEqual(tags[3].slug, 'tag-1')
25+
26+
def test_tag_related_manager_ordering_weight_then_name(self):
27+
tags = [
28+
Tag.objects.create(name='Tag 1', slug='tag-1', weight=100),
29+
Tag.objects.create(name='Tag 2', slug='tag-2'),
30+
Tag.objects.create(name='Tag 3', slug='tag-3', weight=10),
31+
Tag.objects.create(name='Tag 4', slug='tag-4', weight=10),
32+
]
33+
34+
site = Site.objects.create(name='Site 1')
35+
for tag in tags:
36+
site.tags.add(tag)
37+
site.save()
38+
39+
site = Site.objects.first()
40+
tags = site.tags.all()
41+
42+
self.assertEqual(tags[0].slug, 'tag-2')
43+
self.assertEqual(tags[1].slug, 'tag-3')
44+
self.assertEqual(tags[2].slug, 'tag-4')
45+
self.assertEqual(tags[3].slug, 'tag-1')
46+
1347
def test_create_tag_unicode(self):
1448
tag = Tag(name='Testing Unicode: 台灣')
1549
tag.save()

netbox/extras/tests/test_views.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,8 @@ def setUpTestData(cls):
441441

442442
tags = (
443443
Tag(name='Tag 1', slug='tag-1'),
444-
Tag(name='Tag 2', slug='tag-2'),
445-
Tag(name='Tag 3', slug='tag-3'),
444+
Tag(name='Tag 2', slug='tag-2', weight=1),
445+
Tag(name='Tag 3', slug='tag-3', weight=32767),
446446
)
447447
Tag.objects.bulk_create(tags)
448448

@@ -451,13 +451,14 @@ def setUpTestData(cls):
451451
'slug': 'tag-x',
452452
'color': 'c0c0c0',
453453
'comments': 'Some comments',
454+
'weight': 11,
454455
}
455456

456457
cls.csv_data = (
457-
"name,slug,color,description",
458-
"Tag 4,tag-4,ff0000,Fourth tag",
459-
"Tag 5,tag-5,00ff00,Fifth tag",
460-
"Tag 6,tag-6,0000ff,Sixth tag",
458+
"name,slug,color,description,weight",
459+
"Tag 4,tag-4,ff0000,Fourth tag,0",
460+
"Tag 5,tag-5,00ff00,Fifth tag,1111",
461+
"Tag 6,tag-6,0000ff,Sixth tag,0",
461462
)
462463

463464
cls.csv_update_data = (

netbox/netbox/models/features.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,8 @@ class TagsMixin(models.Model):
455455
which is a `TaggableManager` instance.
456456
"""
457457
tags = TaggableManager(
458-
through='extras.TaggedItem'
458+
through='extras.TaggedItem',
459+
ordering=('weight', 'name'),
459460
)
460461

461462
class Meta:

netbox/templates/extras/tag.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ <h2 class="card-header">{% trans "Tag" %}</h2>
2828
<span class="color-label" style="background-color: #{{ object.color }}">&nbsp;</span>
2929
</td>
3030
</tr>
31+
<tr>
32+
<th scope="row">{% trans "Weight" %}</th>
33+
<td>{{ object.weight }}</td>
34+
</tr>
3135
<tr>
3236
<th scope="row">{% trans "Tagged Items" %}</th>
3337
<td>

0 commit comments

Comments
 (0)