mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
initial pass on migrating to custom tag model with color and comments fields
This commit is contained in:
@ -10,6 +10,11 @@ context data may observe a performance drop when returning multiple objects. To
|
|||||||
Config Context is not needed, the query parameter `?exclude=config_context` may be added to the request as to remove
|
Config Context is not needed, the query parameter `?exclude=config_context` may be added to the request as to remove
|
||||||
the Config Context from being included in any results.
|
the Config Context from being included in any results.
|
||||||
|
|
||||||
|
## Enhancements
|
||||||
|
|
||||||
|
* [#2324](https://github.com/digitalocean/netbox/issues/2324) - Add color option for tags
|
||||||
|
* [#2791](https://github.com/digitalocean/netbox/issues/2791) - Add a comment field for tags
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
v2.5.7 (FUTURE)
|
v2.5.7 (FUTURE)
|
||||||
|
25
netbox/circuits/migrations/0015_custom_tag_models.py
Normal file
25
netbox/circuits/migrations/0015_custom_tag_models.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 2.1.4 on 2019-02-20 06:56
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import taggit.managers
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('circuits', '0014_circuittermination_description'),
|
||||||
|
('extras', '0018_rename_tag_tables'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='circuit',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='provider',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
]
|
@ -6,7 +6,7 @@ from taggit.managers import TaggableManager
|
|||||||
from dcim.constants import CONNECTION_STATUS_CHOICES, STATUS_CLASSES
|
from dcim.constants import CONNECTION_STATUS_CHOICES, STATUS_CLASSES
|
||||||
from dcim.fields import ASNField
|
from dcim.fields import ASNField
|
||||||
from dcim.models import CableTermination
|
from dcim.models import CableTermination
|
||||||
from extras.models import CustomFieldModel, ObjectChange
|
from extras.models import CustomFieldModel, ObjectChange, TaggedItem
|
||||||
from utilities.models import ChangeLoggedModel
|
from utilities.models import ChangeLoggedModel
|
||||||
from utilities.utils import serialize_object
|
from utilities.utils import serialize_object
|
||||||
from .constants import CIRCUIT_STATUS_ACTIVE, CIRCUIT_STATUS_CHOICES, TERM_SIDE_CHOICES
|
from .constants import CIRCUIT_STATUS_ACTIVE, CIRCUIT_STATUS_CHOICES, TERM_SIDE_CHOICES
|
||||||
@ -55,7 +55,7 @@ class Provider(ChangeLoggedModel, CustomFieldModel):
|
|||||||
object_id_field='obj_id'
|
object_id_field='obj_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
|
csv_headers = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
|
|||||||
object_id_field='obj_id'
|
object_id_field='obj_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
|
'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
|
||||||
|
85
netbox/dcim/migrations/0070_custom_tag_models.py
Normal file
85
netbox/dcim/migrations/0070_custom_tag_models.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# Generated by Django 2.1.4 on 2019-02-20 06:56
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import taggit.managers
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0069_deprecate_nullablecharfield'),
|
||||||
|
('extras', '0018_rename_tag_tables'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='consoleport',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='consoleserverport',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='device',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='devicebay',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='devicetype',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='frontport',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='interface',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='inventoryitem',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='poweroutlet',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='powerport',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='rack',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='rearport',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='site',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='virtualchassis',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
]
|
@ -15,7 +15,7 @@ from mptt.models import MPTTModel, TreeForeignKey
|
|||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
from timezone_field import TimeZoneField
|
from timezone_field import TimeZoneField
|
||||||
|
|
||||||
from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange
|
from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange, TaggedItem
|
||||||
from utilities.fields import ColorField
|
from utilities.fields import ColorField
|
||||||
from utilities.managers import NaturalOrderingManager
|
from utilities.managers import NaturalOrderingManager
|
||||||
from utilities.models import ChangeLoggedModel
|
from utilities.models import ChangeLoggedModel
|
||||||
@ -319,7 +319,7 @@ class Site(ChangeLoggedModel, CustomFieldModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
objects = NaturalOrderingManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description', 'physical_address',
|
'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description', 'physical_address',
|
||||||
@ -566,7 +566,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
objects = NaturalOrderingManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'site', 'group_name', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag', 'width',
|
'site', 'group_name', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag', 'width',
|
||||||
@ -914,7 +914,7 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
|
|||||||
object_id_field='obj_id'
|
object_id_field='obj_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'comments',
|
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'comments',
|
||||||
@ -1455,7 +1455,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = NaturalOrderingManager()
|
objects = NaturalOrderingManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status',
|
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status',
|
||||||
@ -1743,7 +1743,7 @@ class ConsolePort(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = DeviceComponentManager()
|
objects = DeviceComponentManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name']
|
csv_headers = ['device', 'name']
|
||||||
|
|
||||||
@ -1786,7 +1786,7 @@ class ConsoleServerPort(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = DeviceComponentManager()
|
objects = DeviceComponentManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name']
|
csv_headers = ['device', 'name']
|
||||||
|
|
||||||
@ -1835,7 +1835,7 @@ class PowerPort(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = DeviceComponentManager()
|
objects = DeviceComponentManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name']
|
csv_headers = ['device', 'name']
|
||||||
|
|
||||||
@ -1878,7 +1878,7 @@ class PowerOutlet(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = DeviceComponentManager()
|
objects = DeviceComponentManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name']
|
csv_headers = ['device', 'name']
|
||||||
|
|
||||||
@ -1998,7 +1998,7 @@ class Interface(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = InterfaceManager()
|
objects = InterfaceManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'device', 'virtual_machine', 'name', 'lag', 'form_factor', 'enabled', 'mac_address', 'mtu', 'mgmt_only',
|
'device', 'virtual_machine', 'name', 'lag', 'form_factor', 'enabled', 'mac_address', 'mtu', 'mgmt_only',
|
||||||
@ -2199,7 +2199,7 @@ class FrontPort(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = DeviceComponentManager()
|
objects = DeviceComponentManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'rear_port', 'rear_port_position', 'description']
|
csv_headers = ['device', 'name', 'type', 'rear_port', 'rear_port_position', 'description']
|
||||||
|
|
||||||
@ -2265,7 +2265,7 @@ class RearPort(CableTermination, ComponentModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = DeviceComponentManager()
|
objects = DeviceComponentManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'type', 'positions', 'description']
|
csv_headers = ['device', 'name', 'type', 'positions', 'description']
|
||||||
|
|
||||||
@ -2312,7 +2312,7 @@ class DeviceBay(ComponentModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = DeviceComponentManager()
|
objects = DeviceComponentManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'name', 'installed_device']
|
csv_headers = ['device', 'name', 'installed_device']
|
||||||
|
|
||||||
@ -2405,7 +2405,7 @@ class InventoryItem(ComponentModel):
|
|||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'device', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description',
|
'device', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description',
|
||||||
@ -2452,7 +2452,7 @@ class VirtualChassis(ChangeLoggedModel):
|
|||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['master', 'domain']
|
csv_headers = ['master', 'domain']
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from taggit.models import Tag
|
|
||||||
|
|
||||||
from dcim.api.nested_serializers import (
|
from dcim.api.nested_serializers import (
|
||||||
NestedDeviceSerializer, NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedRackSerializer,
|
NestedDeviceSerializer, NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedRackSerializer,
|
||||||
@ -10,6 +9,7 @@ from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site
|
|||||||
from extras.constants import *
|
from extras.constants import *
|
||||||
from extras.models import (
|
from extras.models import (
|
||||||
ConfigContext, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap,
|
ConfigContext, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap,
|
||||||
|
Tag
|
||||||
)
|
)
|
||||||
from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer
|
from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
@ -80,7 +80,7 @@ class TagSerializer(ValidatedModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tag
|
model = Tag
|
||||||
fields = ['id', 'name', 'slug', 'tagged_items']
|
fields = ['id', 'name', 'slug', 'color', 'comments', 'tagged_items']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -6,11 +6,11 @@ from rest_framework.decorators import action
|
|||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet
|
from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet
|
||||||
from taggit.models import Tag
|
|
||||||
|
|
||||||
from extras import filters
|
from extras import filters
|
||||||
from extras.models import (
|
from extras.models import (
|
||||||
ConfigContext, CustomField, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap,
|
ConfigContext, CustomField, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap,
|
||||||
|
Tag
|
||||||
)
|
)
|
||||||
from extras.reports import get_report, get_reports
|
from extras.reports import get_report, get_reports
|
||||||
from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, ModelViewSet
|
from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, ModelViewSet
|
||||||
@ -115,7 +115,7 @@ class TopologyMapViewSet(ModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class TagViewSet(ModelViewSet):
|
class TagViewSet(ModelViewSet):
|
||||||
queryset = Tag.objects.annotate(tagged_items=Count('taggit_taggeditem_items'))
|
queryset = Tag.objects.annotate(tagged_items=Count('extras_taggeditem_items'))
|
||||||
serializer_class = serializers.TagSerializer
|
serializer_class = serializers.TagSerializer
|
||||||
filterset_class = filters.TagFilter
|
filterset_class = filters.TagFilter
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from taggit.models import Tag
|
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Platform, Region, Site
|
from dcim.models import DeviceRole, Platform, Region, Site
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from .constants import CF_FILTER_DISABLED, CF_FILTER_EXACT, CF_TYPE_BOOLEAN, CF_TYPE_SELECT
|
from .constants import CF_FILTER_DISABLED, CF_FILTER_EXACT, CF_TYPE_BOOLEAN, CF_TYPE_SELECT
|
||||||
from .models import ConfigContext, CustomField, Graph, ExportTemplate, ObjectChange, TopologyMap
|
from .models import ConfigContext, CustomField, Graph, ExportTemplate, ObjectChange, Tag, TopologyMap
|
||||||
|
|
||||||
|
|
||||||
class CustomFieldFilter(django_filters.Filter):
|
class CustomFieldFilter(django_filters.Filter):
|
||||||
|
@ -6,19 +6,18 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from mptt.forms import TreeNodeMultipleChoiceField
|
from mptt.forms import TreeNodeMultipleChoiceField
|
||||||
from taggit.forms import TagField
|
from taggit.forms import TagField
|
||||||
from taggit.models import Tag
|
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Platform, Region, Site
|
from dcim.models import DeviceRole, Platform, Region, Site
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ContentTypeSelect,
|
add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ContentTypeSelect,
|
||||||
FilterChoiceField, FilterTreeNodeMultipleChoiceField, LaxURLField, JSONField, SlugField,
|
FilterChoiceField, FilterTreeNodeMultipleChoiceField, LaxURLField, JSONField, SlugField, CommentField
|
||||||
)
|
)
|
||||||
from .constants import (
|
from .constants import (
|
||||||
CF_FILTER_DISABLED, CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_INTEGER, CF_TYPE_SELECT, CF_TYPE_URL,
|
CF_FILTER_DISABLED, CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_INTEGER, CF_TYPE_SELECT, CF_TYPE_URL,
|
||||||
OBJECTCHANGE_ACTION_CHOICES,
|
OBJECTCHANGE_ACTION_CHOICES,
|
||||||
)
|
)
|
||||||
from .models import ConfigContext, CustomField, CustomFieldValue, ImageAttachment, ObjectChange
|
from .models import ConfigContext, CustomField, CustomFieldValue, ImageAttachment, ObjectChange, Tag
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -190,11 +189,12 @@ class CustomFieldFilterForm(forms.Form):
|
|||||||
|
|
||||||
class TagForm(BootstrapMixin, forms.ModelForm):
|
class TagForm(BootstrapMixin, forms.ModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
comments = CommentField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tag
|
model = Tag
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'slug',
|
'name', 'slug', 'color', 'comments'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
46
netbox/extras/migrations/0017_tag_taggeditem.py
Normal file
46
netbox/extras/migrations/0017_tag_taggeditem.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Generated by Django 2.1.4 on 2019-02-20 06:56
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import utilities.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('contenttypes', '0002_remove_content_type_name'),
|
||||||
|
('extras', '0016_exporttemplate_add_cable'),
|
||||||
|
]
|
||||||
|
|
||||||
|
state_operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Tag',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=100, unique=True)),
|
||||||
|
('slug', models.SlugField(max_length=100, unique=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TaggedItem',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||||
|
('object_id', models.IntegerField(db_index=True)),
|
||||||
|
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='extras_taggeditem_tagged_items', to='contenttypes.ContentType')),
|
||||||
|
('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='extras_taggeditem_items', to='extras.Tag')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.SeparateDatabaseAndState(
|
||||||
|
database_operations=None,
|
||||||
|
state_operations=state_operations
|
||||||
|
)
|
||||||
|
]
|
46
netbox/extras/migrations/0018_rename_tag_tables.py
Normal file
46
netbox/extras/migrations/0018_rename_tag_tables.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Generated by Django 2.1.4 on 2019-02-20 06:59
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class AppTaggitAlterModelTable(migrations.AlterModelTable):
|
||||||
|
"""
|
||||||
|
A special subclass of AlterModelTable which hardcodes the app_label to 'taggit'
|
||||||
|
|
||||||
|
This is needed because the migration deals with models which belong to the taggit
|
||||||
|
app, however because taggit is a 3rd party app, we cannot create our own migrations
|
||||||
|
there.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def state_forwards(self, app_label, state):
|
||||||
|
super().state_forwards('taggit', state)
|
||||||
|
|
||||||
|
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||||
|
super().database_forwards('taggit', schema_editor, from_state, to_state)
|
||||||
|
|
||||||
|
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||||
|
super().database_backwards('taggit', schema_editor, from_state, to_state)
|
||||||
|
|
||||||
|
def reduce(self, operation, app_label=None):
|
||||||
|
if app_label:
|
||||||
|
app_label = 'taggit'
|
||||||
|
super().reduce(operation, app_label=app_label)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('taggit', '0001_initial'),
|
||||||
|
('extras', '0017_tag_taggeditem'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
AppTaggitAlterModelTable(
|
||||||
|
name='Tag',
|
||||||
|
table='extras_tag'
|
||||||
|
),
|
||||||
|
AppTaggitAlterModelTable(
|
||||||
|
name='TaggedItem',
|
||||||
|
table='extras_taggeditem'
|
||||||
|
),
|
||||||
|
]
|
52
netbox/extras/migrations/0019_delete_taggit_models.py
Normal file
52
netbox/extras/migrations/0019_delete_taggit_models.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Generated by Django 2.1.4 on 2019-02-20 07:05
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class AppTaggitDeleteModel(migrations.DeleteModel):
|
||||||
|
"""
|
||||||
|
A special subclass of DeleteModel which hardcodes the app_label to 'taggit'
|
||||||
|
|
||||||
|
This is needed because the migration deals with models which belong to the taggit
|
||||||
|
app, however because taggit is a 3rd party app, we cannot create our own migrations
|
||||||
|
there.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def state_forwards(self, app_label, state):
|
||||||
|
super().state_forwards('taggit', state)
|
||||||
|
|
||||||
|
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||||
|
super().database_forwards('taggit', schema_editor, from_state, to_state)
|
||||||
|
|
||||||
|
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||||
|
super().database_backwards('taggit', schema_editor, from_state, to_state)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('extras', '0018_rename_tag_tables'),
|
||||||
|
('circuits', '0015_custom_tag_models'),
|
||||||
|
('dcim', '0070_custom_tag_models'),
|
||||||
|
('ipam', '0025_custom_tag_models'),
|
||||||
|
('secrets', '0006_custom_tag_models'),
|
||||||
|
('tenancy', '0006_custom_tag_models'),
|
||||||
|
('virtualization', '0009_custom_tag_models'),
|
||||||
|
]
|
||||||
|
|
||||||
|
state_operations = [
|
||||||
|
AppTaggitDeleteModel(
|
||||||
|
name='Tag',
|
||||||
|
),
|
||||||
|
AppTaggitDeleteModel(
|
||||||
|
name='TaggedItem',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
database_operations = []
|
||||||
|
operations = [
|
||||||
|
migrations.SeparateDatabaseAndState(
|
||||||
|
database_operations=None,
|
||||||
|
state_operations=state_operations
|
||||||
|
)
|
||||||
|
]
|
24
netbox/extras/migrations/0020_add_color_comments_to_tag.py
Normal file
24
netbox/extras/migrations/0020_add_color_comments_to_tag.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 2.1.4 on 2019-02-20 07:38
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import utilities.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('extras', '0019_delete_taggit_models'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tag',
|
||||||
|
name='color',
|
||||||
|
field=utilities.fields.ColorField(max_length=6),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tag',
|
||||||
|
name='comments',
|
||||||
|
field=models.TextField(blank=True),
|
||||||
|
),
|
||||||
|
]
|
@ -12,8 +12,10 @@ from django.db.models import F, Q
|
|||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.template import Template, Context
|
from django.template import Template, Context
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from taggit.models import TagBase, GenericTaggedItemBase
|
||||||
|
|
||||||
from dcim.constants import CONNECTION_STATUS_CONNECTED
|
from dcim.constants import CONNECTION_STATUS_CONNECTED
|
||||||
|
from utilities.fields import ColorField
|
||||||
from utilities.utils import deepmerge, foreground_color
|
from utilities.utils import deepmerge, foreground_color
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .querysets import ConfigContextQuerySet
|
from .querysets import ConfigContextQuerySet
|
||||||
@ -860,3 +862,23 @@ class ObjectChange(models.Model):
|
|||||||
self.object_repr,
|
self.object_repr,
|
||||||
self.object_data,
|
self.object_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Tags
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class Tag(TagBase):
|
||||||
|
color = ColorField()
|
||||||
|
comments = models.TextField(
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TaggedItem(GenericTaggedItemBase):
|
||||||
|
tag = models.ForeignKey(
|
||||||
|
to=Tag,
|
||||||
|
related_name="%(app_label)s_%(class)s_items",
|
||||||
|
on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
from taggit.models import Tag, TaggedItem
|
|
||||||
|
|
||||||
from utilities.tables import BaseTable, BooleanColumn, ToggleColumn
|
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn
|
||||||
from .models import ConfigContext, ObjectChange
|
from .models import ConfigContext, ObjectChange, Tag, TaggedItem
|
||||||
|
|
||||||
TAG_ACTIONS = """
|
TAG_ACTIONS = """
|
||||||
{% if perms.taggit.change_tag %}
|
{% if perms.taggit.change_tag %}
|
||||||
@ -71,10 +70,11 @@ class TagTable(BaseTable):
|
|||||||
attrs={'td': {'class': 'text-right'}},
|
attrs={'td': {'class': 'text-right'}},
|
||||||
verbose_name=''
|
verbose_name=''
|
||||||
)
|
)
|
||||||
|
color = ColorColumn()
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Tag
|
model = Tag
|
||||||
fields = ('pk', 'name', 'items', 'slug', 'actions')
|
fields = ('pk', 'name', 'items', 'slug', 'color', 'actions')
|
||||||
|
|
||||||
|
|
||||||
class TaggedItemTable(BaseTable):
|
class TaggedItemTable(BaseTable):
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from taggit.models import Tag
|
|
||||||
|
|
||||||
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Platform, Region, Site
|
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Platform, Region, Site
|
||||||
from extras.constants import GRAPH_TYPE_SITE
|
from extras.constants import GRAPH_TYPE_SITE
|
||||||
from extras.models import ConfigContext, Graph, ExportTemplate
|
from extras.models import ConfigContext, Graph, ExportTemplate, Tag
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from utilities.testing import APITestCase
|
from utilities.testing import APITestCase
|
||||||
|
|
||||||
|
@ -4,10 +4,9 @@ import uuid
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from taggit.models import Tag
|
|
||||||
|
|
||||||
from dcim.models import Site
|
from dcim.models import Site
|
||||||
from extras.models import ConfigContext, ObjectChange
|
from extras.models import ConfigContext, ObjectChange, Tag
|
||||||
|
|
||||||
|
|
||||||
class TagTestCase(TestCase):
|
class TagTestCase(TestCase):
|
||||||
|
@ -9,7 +9,6 @@ from django.shortcuts import get_object_or_404, redirect, render
|
|||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from django_tables2 import RequestConfig
|
from django_tables2 import RequestConfig
|
||||||
from taggit.models import Tag, TaggedItem
|
|
||||||
|
|
||||||
from utilities.forms import ConfirmationForm
|
from utilities.forms import ConfirmationForm
|
||||||
from utilities.paginator import EnhancedPaginator
|
from utilities.paginator import EnhancedPaginator
|
||||||
@ -19,7 +18,7 @@ from .forms import (
|
|||||||
ConfigContextForm, ConfigContextBulkEditForm, ConfigContextFilterForm, ImageAttachmentForm, ObjectChangeFilterForm,
|
ConfigContextForm, ConfigContextBulkEditForm, ConfigContextFilterForm, ImageAttachmentForm, ObjectChangeFilterForm,
|
||||||
TagFilterForm, TagForm,
|
TagFilterForm, TagForm,
|
||||||
)
|
)
|
||||||
from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult
|
from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult, Tag, TaggedItem
|
||||||
from .reports import get_report, get_reports
|
from .reports import get_report, get_reports
|
||||||
from .tables import ConfigContextTable, ObjectChangeTable, TagTable, TaggedItemTable
|
from .tables import ConfigContextTable, ObjectChangeTable, TagTable, TaggedItemTable
|
||||||
|
|
||||||
@ -30,7 +29,7 @@ from .tables import ConfigContextTable, ObjectChangeTable, TagTable, TaggedItemT
|
|||||||
|
|
||||||
class TagListView(ObjectListView):
|
class TagListView(ObjectListView):
|
||||||
queryset = Tag.objects.annotate(
|
queryset = Tag.objects.annotate(
|
||||||
items=Count('taggit_taggeditem_items')
|
items=Count('extras_taggeditem_items')
|
||||||
).order_by(
|
).order_by(
|
||||||
'name'
|
'name'
|
||||||
)
|
)
|
||||||
@ -69,22 +68,23 @@ class TagView(View):
|
|||||||
|
|
||||||
|
|
||||||
class TagEditView(PermissionRequiredMixin, ObjectEditView):
|
class TagEditView(PermissionRequiredMixin, ObjectEditView):
|
||||||
permission_required = 'taggit.change_tag'
|
permission_required = 'extras.change_tag'
|
||||||
model = Tag
|
model = Tag
|
||||||
model_form = TagForm
|
model_form = TagForm
|
||||||
default_return_url = 'extras:tag_list'
|
default_return_url = 'extras:tag_list'
|
||||||
|
template_name = 'extras/tag_edit.html'
|
||||||
|
|
||||||
|
|
||||||
class TagDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
class TagDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||||
permission_required = 'taggit.delete_tag'
|
permission_required = 'extras.delete_tag'
|
||||||
model = Tag
|
model = Tag
|
||||||
default_return_url = 'extras:tag_list'
|
default_return_url = 'extras:tag_list'
|
||||||
|
|
||||||
|
|
||||||
class TagBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class TagBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'taggit.delete_tag'
|
permission_required = 'extras.delete_tag'
|
||||||
queryset = Tag.objects.annotate(
|
queryset = Tag.objects.annotate(
|
||||||
items=Count('taggit_taggeditem_items')
|
items=Count('extras_taggeditem_items')
|
||||||
).order_by(
|
).order_by(
|
||||||
'name'
|
'name'
|
||||||
)
|
)
|
||||||
|
45
netbox/ipam/migrations/0025_custom_tag_models.py
Normal file
45
netbox/ipam/migrations/0025_custom_tag_models.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Generated by Django 2.1.4 on 2019-02-20 06:56
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import taggit.managers
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('ipam', '0024_vrf_allow_null_rd'),
|
||||||
|
('extras', '0018_rename_tag_tables'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='aggregate',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ipaddress',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='prefix',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='service',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vlan',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vrf',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
]
|
@ -10,7 +10,7 @@ from django.urls import reverse
|
|||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from dcim.models import Interface
|
from dcim.models import Interface
|
||||||
from extras.models import CustomFieldModel
|
from extras.models import CustomFieldModel, TaggedItem
|
||||||
from utilities.models import ChangeLoggedModel
|
from utilities.models import ChangeLoggedModel
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .fields import IPNetworkField, IPAddressField
|
from .fields import IPNetworkField, IPAddressField
|
||||||
@ -55,7 +55,7 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
|
|||||||
object_id_field='obj_id'
|
object_id_field='obj_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['name', 'rd', 'tenant', 'enforce_unique', 'description']
|
csv_headers = ['name', 'rd', 'tenant', 'enforce_unique', 'description']
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
|
|||||||
object_id_field='obj_id'
|
object_id_field='obj_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['prefix', 'rir', 'date_added', 'description']
|
csv_headers = ['prefix', 'rir', 'date_added', 'description']
|
||||||
|
|
||||||
@ -324,7 +324,7 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = PrefixQuerySet.as_manager()
|
objects = PrefixQuerySet.as_manager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'prefix', 'vrf', 'tenant', 'site', 'vlan_group', 'vlan_vid', 'status', 'role', 'is_pool', 'description',
|
'prefix', 'vrf', 'tenant', 'site', 'vlan_group', 'vlan_vid', 'status', 'role', 'is_pool', 'description',
|
||||||
@ -583,7 +583,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = IPAddressManager()
|
objects = IPAddressManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface_name', 'is_primary',
|
'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface_name', 'is_primary',
|
||||||
@ -790,7 +790,7 @@ class VLAN(ChangeLoggedModel, CustomFieldModel):
|
|||||||
object_id_field='obj_id'
|
object_id_field='obj_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['site', 'group_name', 'vid', 'name', 'tenant', 'status', 'role', 'description']
|
csv_headers = ['site', 'group_name', 'vid', 'name', 'tenant', 'status', 'role', 'description']
|
||||||
|
|
||||||
@ -892,7 +892,7 @@ class Service(ChangeLoggedModel, CustomFieldModel):
|
|||||||
object_id_field='obj_id'
|
object_id_field='obj_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['device', 'virtual_machine', 'name', 'protocol', 'description']
|
csv_headers = ['device', 'virtual_machine', 'name', 'protocol', 'description']
|
||||||
|
|
||||||
|
@ -2,8 +2,17 @@ from django.conf import settings
|
|||||||
from django.contrib.admin import AdminSite
|
from django.contrib.admin import AdminSite
|
||||||
from django.contrib.auth.admin import GroupAdmin, UserAdmin
|
from django.contrib.auth.admin import GroupAdmin, UserAdmin
|
||||||
from django.contrib.auth.models import Group, User
|
from django.contrib.auth.models import Group, User
|
||||||
from taggit.admin import TagAdmin
|
from taggit.admin import TagAdmin, TaggedItemInline
|
||||||
from taggit.models import Tag
|
|
||||||
|
from extras.models import Tag, TaggedItem
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxTaggedItemInline(TaggedItemInline):
|
||||||
|
model = TaggedItem
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxTagAdmin(TagAdmin):
|
||||||
|
inlines = [NetBoxTaggedItemInline]
|
||||||
|
|
||||||
|
|
||||||
class NetBoxAdminSite(AdminSite):
|
class NetBoxAdminSite(AdminSite):
|
||||||
@ -20,7 +29,7 @@ admin_site = NetBoxAdminSite(name='admin')
|
|||||||
# Register external models
|
# Register external models
|
||||||
admin_site.register(Group, GroupAdmin)
|
admin_site.register(Group, GroupAdmin)
|
||||||
admin_site.register(User, UserAdmin)
|
admin_site.register(User, UserAdmin)
|
||||||
admin_site.register(Tag, TagAdmin)
|
admin_site.register(Tag, NetBoxTagAdmin)
|
||||||
|
|
||||||
# Modify the template to include an RQ link if django_rq is installed (see RQ_SHOW_ADMIN_LINK)
|
# Modify the template to include an RQ link if django_rq is installed (see RQ_SHOW_ADMIN_LINK)
|
||||||
if settings.WEBHOOKS_ENABLED:
|
if settings.WEBHOOKS_ENABLED:
|
||||||
|
20
netbox/secrets/migrations/0006_custom_tag_models.py
Normal file
20
netbox/secrets/migrations/0006_custom_tag_models.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 2.1.4 on 2019-02-20 06:56
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import taggit.managers
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('secrets', '0005_change_logging'),
|
||||||
|
('extras', '0018_rename_tag_tables'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='secret',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
]
|
@ -14,7 +14,7 @@ from django.urls import reverse
|
|||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from extras.models import CustomFieldModel
|
from extras.models import CustomFieldModel, TaggedItem
|
||||||
from utilities.models import ChangeLoggedModel
|
from utilities.models import ChangeLoggedModel
|
||||||
from .exceptions import InvalidKey
|
from .exceptions import InvalidKey
|
||||||
from .hashers import SecretValidationHasher
|
from .hashers import SecretValidationHasher
|
||||||
@ -345,7 +345,7 @@ class Secret(ChangeLoggedModel, CustomFieldModel):
|
|||||||
object_id_field='obj_id'
|
object_id_field='obj_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
plaintext = None
|
plaintext = None
|
||||||
csv_headers = ['device', 'role', 'name', 'plaintext']
|
csv_headers = ['device', 'role', 'name', 'plaintext']
|
||||||
|
@ -59,8 +59,26 @@
|
|||||||
{{ items_count }}
|
{{ items_count }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Color</td>
|
||||||
|
<td>
|
||||||
|
<span class="label color-block" style="background-color: #{{ tag.color }}"> </span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Comments</strong>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body rendered-markdown">
|
||||||
|
{% if tag.comments %}
|
||||||
|
{{ tag.comments|gfm }}
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">None</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{% include 'panel_table.html' with table=items_table heading='Tagged Objects' %}
|
{% include 'panel_table.html' with table=items_table heading='Tagged Objects' %}
|
||||||
|
19
netbox/templates/extras/tag_edit.html
Normal file
19
netbox/templates/extras/tag_edit.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{% extends 'utilities/obj_edit.html' %}
|
||||||
|
{% load form_helpers %}
|
||||||
|
|
||||||
|
{% block form %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading"><strong>Tag</strong></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{% render_field form.name %}
|
||||||
|
{% render_field form.slug %}
|
||||||
|
{% render_field form.color %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading"><strong>Comments</strong></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{% render_field form.comments %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -1,5 +1,7 @@
|
|||||||
|
{% load helpers %}
|
||||||
|
|
||||||
{% if url_name %}
|
{% if url_name %}
|
||||||
<a href="{% url url_name %}?tag={{ tag.slug }}"><span class="label label-default">{{ tag }}</span></a>
|
<a href="{% url url_name %}?tag={{ tag.slug }}"><span class="label label-default" style="color: {{ tag.color|fgcolor }}; background-color: #{{ tag.color }}">{{ tag }}</span></a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="label label-default">{{ tag }}</span>
|
<span class="label label-default">{{ tag }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
20
netbox/tenancy/migrations/0006_custom_tag_models.py
Normal file
20
netbox/tenancy/migrations/0006_custom_tag_models.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 2.1.4 on 2019-02-20 06:56
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import taggit.managers
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('tenancy', '0005_change_logging'),
|
||||||
|
('extras', '0018_rename_tag_tables'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='tenant',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
]
|
@ -3,7 +3,7 @@ from django.db import models
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from extras.models import CustomFieldModel
|
from extras.models import CustomFieldModel, TaggedItem
|
||||||
from utilities.models import ChangeLoggedModel
|
from utilities.models import ChangeLoggedModel
|
||||||
|
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ class Tenant(ChangeLoggedModel, CustomFieldModel):
|
|||||||
object_id_field='obj_id'
|
object_id_field='obj_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['name', 'slug', 'group', 'description', 'comments']
|
csv_headers = ['name', 'slug', 'group', 'description', 'comments']
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from taggit.models import Tag
|
|
||||||
|
from extras.models import Tag
|
||||||
|
|
||||||
|
|
||||||
class NumericInFilter(django_filters.BaseInFilter, django_filters.NumberFilter):
|
class NumericInFilter(django_filters.BaseInFilter, django_filters.NumberFilter):
|
||||||
|
@ -157,7 +157,7 @@ class ObjectListView(View):
|
|||||||
|
|
||||||
# Construct queryset for tags list
|
# Construct queryset for tags list
|
||||||
if hasattr(model, 'tags'):
|
if hasattr(model, 'tags'):
|
||||||
tags = model.tags.annotate(count=Count('taggit_taggeditem_items')).order_by('name')
|
tags = model.tags.annotate(count=Count('extras_taggeditem_items')).order_by('name')
|
||||||
else:
|
else:
|
||||||
tags = None
|
tags = None
|
||||||
|
|
||||||
|
25
netbox/virtualization/migrations/0009_custom_tag_models.py
Normal file
25
netbox/virtualization/migrations/0009_custom_tag_models.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 2.1.4 on 2019-02-20 06:56
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import taggit.managers
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('virtualization', '0008_virtualmachine_local_context_data'),
|
||||||
|
('extras', '0018_rename_tag_tables'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='cluster',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='virtualmachine',
|
||||||
|
name='tags',
|
||||||
|
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||||
|
),
|
||||||
|
]
|
@ -6,7 +6,7 @@ from django.urls import reverse
|
|||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from dcim.models import Device
|
from dcim.models import Device
|
||||||
from extras.models import ConfigContextModel, CustomFieldModel
|
from extras.models import ConfigContextModel, CustomFieldModel, TaggedItem
|
||||||
from utilities.models import ChangeLoggedModel
|
from utilities.models import ChangeLoggedModel
|
||||||
from .constants import DEVICE_STATUS_ACTIVE, VM_STATUS_CHOICES, VM_STATUS_CLASSES
|
from .constants import DEVICE_STATUS_ACTIVE, VM_STATUS_CHOICES, VM_STATUS_CLASSES
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ class Cluster(ChangeLoggedModel, CustomFieldModel):
|
|||||||
object_id_field='obj_id'
|
object_id_field='obj_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = ['name', 'type', 'group', 'site', 'comments']
|
csv_headers = ['name', 'type', 'group', 'site', 'comments']
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
object_id_field='obj_id'
|
object_id_field='obj_id'
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'name', 'status', 'role', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
|
'name', 'status', 'role', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
|
||||||
|
Reference in New Issue
Block a user