mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 01:41:22 -06:00
parent
d25605c261
commit
6b7d23d684
@ -16,6 +16,12 @@ A unique URL-friendly identifier. (This value will be used for filtering.) This
|
||||
|
||||
The color to use when displaying the tag in the NetBox UI.
|
||||
|
||||
### Weight
|
||||
|
||||
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**.
|
||||
|
||||
!!! info "This field was introduced in NetBox v4.3."
|
||||
|
||||
### Object Types
|
||||
|
||||
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.
|
||||
|
@ -27,8 +27,8 @@ class TagSerializer(ValidatedModelSerializer):
|
||||
class Meta:
|
||||
model = Tag
|
||||
fields = [
|
||||
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'object_types',
|
||||
'tagged_items', 'created', 'last_updated',
|
||||
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'weight',
|
||||
'object_types', 'tagged_items', 'created', 'last_updated',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'color', 'description')
|
||||
|
||||
|
@ -450,7 +450,7 @@ class TagFilterSet(ChangeLoggedModelFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Tag
|
||||
fields = ('id', 'name', 'slug', 'color', 'description', 'object_types')
|
||||
fields = ('id', 'name', 'slug', 'color', 'weight', 'description', 'object_types')
|
||||
|
||||
def search(self, queryset, name, value):
|
||||
if not value.strip():
|
||||
|
@ -275,6 +275,10 @@ class TagBulkEditForm(BulkEditForm):
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
weight = forms.IntegerField(
|
||||
label=_('Weight'),
|
||||
required=False
|
||||
)
|
||||
|
||||
nullable_fields = ('description',)
|
||||
|
||||
|
@ -232,10 +232,14 @@ class EventRuleImportForm(NetBoxModelImportForm):
|
||||
|
||||
class TagImportForm(CSVModelForm):
|
||||
slug = SlugField()
|
||||
weight = forms.IntegerField(
|
||||
label=_('Weight'),
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Tag
|
||||
fields = ('name', 'slug', 'color', 'description')
|
||||
fields = ('name', 'slug', 'color', 'weight', 'description')
|
||||
|
||||
|
||||
class JournalEntryImportForm(NetBoxModelImportForm):
|
||||
|
@ -490,15 +490,19 @@ class TagForm(forms.ModelForm):
|
||||
queryset=ObjectType.objects.with_feature('tags'),
|
||||
required=False
|
||||
)
|
||||
weight = forms.IntegerField(
|
||||
label=_('Weight'),
|
||||
required=False
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
FieldSet('name', 'slug', 'color', 'description', 'object_types', name=_('Tag')),
|
||||
FieldSet('name', 'slug', 'color', 'weight', 'description', 'object_types', name=_('Tag')),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Tag
|
||||
fields = [
|
||||
'name', 'slug', 'color', 'description', 'object_types',
|
||||
'name', 'slug', 'color', 'weight', 'description', 'object_types',
|
||||
]
|
||||
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
# Generated by Django 5.2b1 on 2025-03-17 14:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('extras', '0123_remove_staging'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='tag',
|
||||
options={'ordering': ('weight', 'name')},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='tag',
|
||||
name='weight',
|
||||
field=models.PositiveSmallIntegerField(default=0),
|
||||
),
|
||||
]
|
@ -40,13 +40,17 @@ class Tag(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, TagBase):
|
||||
blank=True,
|
||||
help_text=_("The object type(s) to which this tag can be applied.")
|
||||
)
|
||||
weight = models.PositiveSmallIntegerField(
|
||||
verbose_name=_('weight'),
|
||||
default=0,
|
||||
)
|
||||
|
||||
clone_fields = (
|
||||
'color', 'description', 'object_types',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
ordering = ('weight', 'name')
|
||||
verbose_name = _('tag')
|
||||
verbose_name_plural = _('tags')
|
||||
|
||||
|
@ -449,8 +449,8 @@ class TagTable(NetBoxTable):
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = Tag
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'items', 'slug', 'color', 'description', 'object_types', 'created', 'last_updated',
|
||||
'actions',
|
||||
'pk', 'id', 'name', 'items', 'slug', 'color', 'weight', 'description', 'object_types',
|
||||
'created', 'last_updated', 'actions',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'items', 'slug', 'color', 'description')
|
||||
|
||||
|
@ -513,6 +513,7 @@ class TagTest(APIViewTestCases.APIViewTestCase):
|
||||
{
|
||||
'name': 'Tag 4',
|
||||
'slug': 'tag-4',
|
||||
'weight': 1000,
|
||||
},
|
||||
{
|
||||
'name': 'Tag 5',
|
||||
@ -533,7 +534,7 @@ class TagTest(APIViewTestCases.APIViewTestCase):
|
||||
tags = (
|
||||
Tag(name='Tag 1', slug='tag-1'),
|
||||
Tag(name='Tag 2', slug='tag-2'),
|
||||
Tag(name='Tag 3', slug='tag-3'),
|
||||
Tag(name='Tag 3', slug='tag-3', weight=26),
|
||||
)
|
||||
Tag.objects.bulk_create(tags)
|
||||
|
||||
|
@ -1196,7 +1196,7 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
tags = (
|
||||
Tag(name='Tag 1', slug='tag-1', color='ff0000', description='foobar1'),
|
||||
Tag(name='Tag 2', slug='tag-2', color='00ff00', description='foobar2'),
|
||||
Tag(name='Tag 3', slug='tag-3', color='0000ff'),
|
||||
Tag(name='Tag 3', slug='tag-3', color='0000ff', weight=1000),
|
||||
)
|
||||
Tag.objects.bulk_create(tags)
|
||||
tags[0].object_types.add(object_types['site'])
|
||||
@ -1249,6 +1249,13 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
['Tag 2', 'Tag 3']
|
||||
)
|
||||
|
||||
def test_weight(self):
|
||||
params = {'weight': [1000]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
|
||||
params = {'weight': [0]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
|
||||
class TaggedItemFilterSetTestCase(TestCase):
|
||||
queryset = TaggedItem.objects.all()
|
||||
|
@ -10,6 +10,40 @@ from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMac
|
||||
|
||||
class TagTest(TestCase):
|
||||
|
||||
def test_default_ordering_weight_then_name_is_set(self):
|
||||
Tag.objects.create(name='Tag 1', slug='tag-1', weight=100)
|
||||
Tag.objects.create(name='Tag 2', slug='tag-2')
|
||||
Tag.objects.create(name='Tag 3', slug='tag-3', weight=10)
|
||||
Tag.objects.create(name='Tag 4', slug='tag-4', weight=10)
|
||||
|
||||
tags = Tag.objects.all()
|
||||
|
||||
self.assertEqual(tags[0].slug, 'tag-2')
|
||||
self.assertEqual(tags[1].slug, 'tag-3')
|
||||
self.assertEqual(tags[2].slug, 'tag-4')
|
||||
self.assertEqual(tags[3].slug, 'tag-1')
|
||||
|
||||
def test_tag_related_manager_ordering_weight_then_name(self):
|
||||
tags = [
|
||||
Tag.objects.create(name='Tag 1', slug='tag-1', weight=100),
|
||||
Tag.objects.create(name='Tag 2', slug='tag-2'),
|
||||
Tag.objects.create(name='Tag 3', slug='tag-3', weight=10),
|
||||
Tag.objects.create(name='Tag 4', slug='tag-4', weight=10),
|
||||
]
|
||||
|
||||
site = Site.objects.create(name='Site 1')
|
||||
for tag in tags:
|
||||
site.tags.add(tag)
|
||||
site.save()
|
||||
|
||||
site = Site.objects.first()
|
||||
tags = site.tags.all()
|
||||
|
||||
self.assertEqual(tags[0].slug, 'tag-2')
|
||||
self.assertEqual(tags[1].slug, 'tag-3')
|
||||
self.assertEqual(tags[2].slug, 'tag-4')
|
||||
self.assertEqual(tags[3].slug, 'tag-1')
|
||||
|
||||
def test_create_tag_unicode(self):
|
||||
tag = Tag(name='Testing Unicode: 台灣')
|
||||
tag.save()
|
||||
|
@ -441,8 +441,8 @@ class TagTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
|
||||
tags = (
|
||||
Tag(name='Tag 1', slug='tag-1'),
|
||||
Tag(name='Tag 2', slug='tag-2'),
|
||||
Tag(name='Tag 3', slug='tag-3'),
|
||||
Tag(name='Tag 2', slug='tag-2', weight=1),
|
||||
Tag(name='Tag 3', slug='tag-3', weight=32767),
|
||||
)
|
||||
Tag.objects.bulk_create(tags)
|
||||
|
||||
@ -451,13 +451,14 @@ class TagTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
'slug': 'tag-x',
|
||||
'color': 'c0c0c0',
|
||||
'comments': 'Some comments',
|
||||
'weight': 11,
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"name,slug,color,description",
|
||||
"Tag 4,tag-4,ff0000,Fourth tag",
|
||||
"Tag 5,tag-5,00ff00,Fifth tag",
|
||||
"Tag 6,tag-6,0000ff,Sixth tag",
|
||||
"name,slug,color,description,weight",
|
||||
"Tag 4,tag-4,ff0000,Fourth tag,0",
|
||||
"Tag 5,tag-5,00ff00,Fifth tag,1111",
|
||||
"Tag 6,tag-6,0000ff,Sixth tag,0",
|
||||
)
|
||||
|
||||
cls.csv_update_data = (
|
||||
|
@ -455,7 +455,8 @@ class TagsMixin(models.Model):
|
||||
which is a `TaggableManager` instance.
|
||||
"""
|
||||
tags = TaggableManager(
|
||||
through='extras.TaggedItem'
|
||||
through='extras.TaggedItem',
|
||||
ordering=('weight', 'name'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
@ -28,6 +28,10 @@
|
||||
<span class="color-label" style="background-color: #{{ object.color }}"> </span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Weight" %}</th>
|
||||
<td>{{ object.weight }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Tagged Items" %}</th>
|
||||
<td>
|
||||
|
Loading…
Reference in New Issue
Block a user