From 8ff3d2cbf644af83385d1964a8e579cf38f2b461 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 28 Aug 2019 11:56:00 -0400 Subject: [PATCH] Closes #3456: Enable bulk editing of tag color --- CHANGELOG.md | 1 + netbox/extras/forms.py | 20 ++++++++++++++-- netbox/extras/urls.py | 1 + netbox/extras/views.py | 33 +++++++++++++++++---------- netbox/templates/extras/tag_list.html | 2 +- 5 files changed, 42 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4b91e704..0c973390e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ v2.6.3 (FUTURE) * [#3405](https://github.com/netbox-community/netbox/issues/3405) - Fix population of power port/outlet details on device creation * [#3422](https://github.com/netbox-community/netbox/issues/3422) - Prevent navigation menu from overlapping page content * [#3430](https://github.com/netbox-community/netbox/issues/3430) - Linkify platform field on device view +* [#3456](https://github.com/netbox-community/netbox/issues/3456) - Enable bulk editing of tag color --- diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index d4cda76d8..b06d53423 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -8,9 +8,10 @@ from taggit.forms import TagField from dcim.models import DeviceRole, Platform, Region, Site from tenancy.models import Tenant, TenantGroup +from utilities.constants import COLOR_CHOICES from utilities.forms import ( - add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, CommentField, - ContentTypeSelect, FilterChoiceField, LaxURLField, JSONField, SlugField, + add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, + CommentField, ContentTypeSelect, FilterChoiceField, LaxURLField, JSONField, SlugField, ) from .constants import ( CF_FILTER_DISABLED, CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_INTEGER, CF_TYPE_SELECT, CF_TYPE_URL, @@ -219,6 +220,21 @@ class TagFilterForm(BootstrapMixin, forms.Form): ) +class TagBulkEditForm(BootstrapMixin, BulkEditForm): + pk = forms.ModelMultipleChoiceField( + queryset=Tag.objects.all(), + widget=forms.MultipleHiddenInput + ) + color = forms.CharField( + max_length=6, + required=False, + widget=ColorSelect() + ) + + class Meta: + nullable_fields = [] + + # # Config contexts # diff --git a/netbox/extras/urls.py b/netbox/extras/urls.py index 7de0faf91..edc3ffcad 100644 --- a/netbox/extras/urls.py +++ b/netbox/extras/urls.py @@ -9,6 +9,7 @@ urlpatterns = [ # Tags path(r'tags/', views.TagListView.as_view(), name='tag_list'), + path(r'tags/edit/', views.TagBulkEditView.as_view(), name='tag_bulk_edit'), path(r'tags/delete/', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'), path(r'tags//', views.TagView.as_view(), name='tag'), path(r'tags//edit/', views.TagEditView.as_view(), name='tag_edit'), diff --git a/netbox/extras/views.py b/netbox/extras/views.py index d3e7e6537..c8dc2f374 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -13,11 +13,7 @@ from django_tables2 import RequestConfig from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator from utilities.views import BulkDeleteView, BulkEditView, ObjectDeleteView, ObjectEditView, ObjectListView -from . import filters -from .forms import ( - ConfigContextForm, ConfigContextBulkEditForm, ConfigContextFilterForm, ImageAttachmentForm, ObjectChangeFilterForm, - TagFilterForm, TagForm, -) +from . import filters, forms from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult, Tag, TaggedItem from .reports import get_report, get_reports from .scripts import get_scripts, run_script @@ -36,7 +32,7 @@ class TagListView(PermissionRequiredMixin, ObjectListView): 'name' ) filter = filters.TagFilter - filter_form = TagFilterForm + filter_form = forms.TagFilterForm table = TagTable template_name = 'extras/tag_list.html' @@ -70,7 +66,7 @@ class TagView(View): class TagEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'extras.change_tag' model = Tag - model_form = TagForm + model_form = forms.TagForm default_return_url = 'extras:tag_list' template_name = 'extras/tag_edit.html' @@ -81,6 +77,19 @@ class TagDeleteView(PermissionRequiredMixin, ObjectDeleteView): default_return_url = 'extras:tag_list' +class TagBulkEditView(PermissionRequiredMixin, BulkEditView): + permission_required = 'extras.change_tag' + queryset = Tag.objects.annotate( + items=Count('extras_taggeditem_items', distinct=True) + ).order_by( + 'name' + ) + # filter = filters.ProviderFilter + table = TagTable + form = forms.TagBulkEditForm + default_return_url = 'circuits:provider_list' + + class TagBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): permission_required = 'extras.delete_tag' queryset = Tag.objects.annotate( @@ -100,7 +109,7 @@ class ConfigContextListView(PermissionRequiredMixin, ObjectListView): permission_required = 'extras.view_configcontext' queryset = ConfigContext.objects.all() filter = filters.ConfigContextFilter - filter_form = ConfigContextFilterForm + filter_form = forms.ConfigContextFilterForm table = ConfigContextTable template_name = 'extras/configcontext_list.html' @@ -120,7 +129,7 @@ class ConfigContextView(PermissionRequiredMixin, View): class ConfigContextCreateView(PermissionRequiredMixin, ObjectEditView): permission_required = 'extras.add_configcontext' model = ConfigContext - model_form = ConfigContextForm + model_form = forms.ConfigContextForm default_return_url = 'extras:configcontext_list' template_name = 'extras/configcontext_edit.html' @@ -134,7 +143,7 @@ class ConfigContextBulkEditView(PermissionRequiredMixin, BulkEditView): queryset = ConfigContext.objects.all() filter = filters.ConfigContextFilter table = ConfigContextTable - form = ConfigContextBulkEditForm + form = forms.ConfigContextBulkEditForm default_return_url = 'extras:configcontext_list' @@ -179,7 +188,7 @@ class ObjectChangeListView(PermissionRequiredMixin, ObjectListView): permission_required = 'extras.view_objectchange' queryset = ObjectChange.objects.prefetch_related('user', 'changed_object_type') filter = filters.ObjectChangeFilter - filter_form = ObjectChangeFilterForm + filter_form = forms.ObjectChangeFilterForm table = ObjectChangeTable template_name = 'extras/objectchange_list.html' @@ -258,7 +267,7 @@ class ObjectChangeLogView(View): class ImageAttachmentEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'extras.change_imageattachment' model = ImageAttachment - model_form = ImageAttachmentForm + model_form = forms.ImageAttachmentForm def alter_obj(self, imageattachment, request, args, kwargs): if not imageattachment.pk: diff --git a/netbox/templates/extras/tag_list.html b/netbox/templates/extras/tag_list.html index 8178e5538..c87b6c2e5 100644 --- a/netbox/templates/extras/tag_list.html +++ b/netbox/templates/extras/tag_list.html @@ -5,7 +5,7 @@

{% block title %}Tags{% endblock %}

- {% include 'utilities/obj_table.html' with bulk_delete_url='extras:tag_bulk_delete' %} + {% include 'utilities/obj_table.html' with bulk_edit_url='extras:tag_bulk_edit' bulk_delete_url='extras:tag_bulk_delete' %}
{% include 'inc/search_panel.html' %}