From cbf4b43b35cb9f780ad485f1fae93d0e538a19eb Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Tue, 1 Aug 2023 21:22:14 +0530 Subject: [PATCH] Adds tags on contact assignment (#13328) * adds tags on contact assignments #12882 * updated migration * added tags on import form * adds TagsMixin on ContactAssignmentType #12882 * Misc cleanup --------- Co-authored-by: Jeremy Stretch --- .../tenancy/contactassignment_edit.html | 1 + netbox/tenancy/api/serializers.py | 4 ++-- netbox/tenancy/filtersets.py | 4 +++- netbox/tenancy/forms/bulk_import.py | 3 --- netbox/tenancy/forms/filtersets.py | 3 ++- netbox/tenancy/forms/model_forms.py | 10 ++++++++-- netbox/tenancy/graphql/types.py | 3 ++- .../migrations/0011_contactassignment_tags.py | 20 +++++++++++++++++++ netbox/tenancy/models/contacts.py | 3 ++- netbox/tenancy/tables/contacts.py | 5 ++++- 10 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 netbox/tenancy/migrations/0011_contactassignment_tags.py diff --git a/netbox/templates/tenancy/contactassignment_edit.html b/netbox/templates/tenancy/contactassignment_edit.html index e9ea1e94e..ef2036976 100644 --- a/netbox/templates/tenancy/contactassignment_edit.html +++ b/netbox/templates/tenancy/contactassignment_edit.html @@ -23,5 +23,6 @@ {% render_field form.contact %} {% render_field form.role %} {% render_field form.priority %} + {% render_field form.tags %} {% endblock %} diff --git a/netbox/tenancy/api/serializers.py b/netbox/tenancy/api/serializers.py index 75d68a4a5..da0ad04bd 100644 --- a/netbox/tenancy/api/serializers.py +++ b/netbox/tenancy/api/serializers.py @@ -104,8 +104,8 @@ class ContactAssignmentSerializer(NetBoxModelSerializer): class Meta: model = ContactAssignment fields = [ - 'id', 'url', 'display', 'content_type', 'object_id', 'object', 'contact', 'role', 'priority', 'created', - 'last_updated', + 'id', 'url', 'display', 'content_type', 'object_id', 'object', 'contact', 'role', 'priority', 'tags', + 'created', 'last_updated', ] @extend_schema_field(OpenApiTypes.OBJECT) diff --git a/netbox/tenancy/filtersets.py b/netbox/tenancy/filtersets.py index 1edc8fdc8..0f4900f54 100644 --- a/netbox/tenancy/filtersets.py +++ b/netbox/tenancy/filtersets.py @@ -2,6 +2,7 @@ import django_filters from django.db.models import Q from django.utils.translation import gettext as _ +from extras.filters import TagFilter from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet from utilities.filters import ContentTypeFilter, TreeNodeMultipleChoiceFilter from .models import * @@ -100,10 +101,11 @@ class ContactAssignmentFilterSet(ChangeLoggedModelFilterSet): to_field_name='slug', label=_('Contact role (slug)'), ) + tag = TagFilter() class Meta: model = ContactAssignment - fields = ['id', 'content_type_id', 'object_id', 'priority'] + fields = ['id', 'content_type_id', 'object_id', 'priority', 'tag'] def search(self, queryset, name, value): if not value.strip(): diff --git a/netbox/tenancy/forms/bulk_import.py b/netbox/tenancy/forms/bulk_import.py index 6a5eb05bf..f38b3293d 100644 --- a/netbox/tenancy/forms/bulk_import.py +++ b/netbox/tenancy/forms/bulk_import.py @@ -106,9 +106,6 @@ class ContactAssignmentImportForm(NetBoxModelImportForm): help_text=_('Assigned role') ) - # Remove the tags field added by NetBoxModelImportForm (unsupported by ContactAssignment) - tags = None - class Meta: model = ContactAssignment fields = ('content_type', 'object_id', 'contact', 'priority', 'role') diff --git a/netbox/tenancy/forms/filtersets.py b/netbox/tenancy/forms/filtersets.py index 6ad473fa8..543c4b1e5 100644 --- a/netbox/tenancy/forms/filtersets.py +++ b/netbox/tenancy/forms/filtersets.py @@ -83,7 +83,7 @@ class ContactFilterForm(NetBoxModelFilterSetForm): class ContactAssignmentFilterForm(NetBoxModelFilterSetForm): model = ContactAssignment fieldsets = ( - (None, ('q', 'filter_id')), + (None, ('q', 'filter_id', 'tag')), (_('Assignment'), ('content_type_id', 'group_id', 'contact_id', 'role_id', 'priority')), ) content_type_id = ContentTypeMultipleChoiceField( @@ -112,3 +112,4 @@ class ContactAssignmentFilterForm(NetBoxModelFilterSetForm): choices=ContactPriorityChoices, required=False ) + tag = TagFilterField(model) diff --git a/netbox/tenancy/forms/model_forms.py b/netbox/tenancy/forms/model_forms.py index 41a202c7f..72c030d84 100644 --- a/netbox/tenancy/forms/model_forms.py +++ b/netbox/tenancy/forms/model_forms.py @@ -1,10 +1,11 @@ from django import forms from django.utils.translation import gettext_lazy as _ +from extras.models import Tag from netbox.forms import NetBoxModelForm from tenancy.models import * from utilities.forms import BootstrapMixin -from utilities.forms.fields import CommentField, DynamicModelChoiceField, SlugField +from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField __all__ = ( 'ContactAssignmentForm', @@ -140,11 +141,16 @@ class ContactAssignmentForm(BootstrapMixin, forms.ModelForm): label=_('Role'), queryset=ContactRole.objects.all() ) + tags = DynamicModelMultipleChoiceField( + queryset=Tag.objects.all(), + required=False, + label=_('Tags') + ) class Meta: model = ContactAssignment fields = ( - 'content_type', 'object_id', 'group', 'contact', 'role', 'priority', + 'content_type', 'object_id', 'group', 'contact', 'role', 'priority', 'tags' ) widgets = { 'content_type': forms.HiddenInput(), diff --git a/netbox/tenancy/graphql/types.py b/netbox/tenancy/graphql/types.py index e0b99c2eb..727aa2eac 100644 --- a/netbox/tenancy/graphql/types.py +++ b/netbox/tenancy/graphql/types.py @@ -1,5 +1,6 @@ import graphene +from extras.graphql.mixins import TagsMixin from tenancy import filtersets, models from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, NetBoxObjectType @@ -68,7 +69,7 @@ class ContactGroupType(OrganizationalObjectType): filterset_class = filtersets.ContactGroupFilterSet -class ContactAssignmentType(BaseObjectType): +class ContactAssignmentType(TagsMixin, BaseObjectType): class Meta: model = models.ContactAssignment diff --git a/netbox/tenancy/migrations/0011_contactassignment_tags.py b/netbox/tenancy/migrations/0011_contactassignment_tags.py new file mode 100644 index 000000000..3c17d9ec1 --- /dev/null +++ b/netbox/tenancy/migrations/0011_contactassignment_tags.py @@ -0,0 +1,20 @@ +# Generated by Django 4.1.10 on 2023-07-08 07:17 + +from django.db import migrations +import taggit.managers + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0097_customfield_remove_choices'), + ('tenancy', '0010_tenant_relax_uniqueness'), + ] + + operations = [ + migrations.AddField( + model_name='contactassignment', + name='tags', + field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'), + ), + ] diff --git a/netbox/tenancy/models/contacts.py b/netbox/tenancy/models/contacts.py index f3440c25d..71c1903ac 100644 --- a/netbox/tenancy/models/contacts.py +++ b/netbox/tenancy/models/contacts.py @@ -5,6 +5,7 @@ from django.urls import reverse from django.utils.translation import gettext_lazy as _ from netbox.models import ChangeLoggedModel, NestedGroupModel, OrganizationalModel, PrimaryModel +from netbox.models.features import TagsMixin from tenancy.choices import * __all__ = ( @@ -99,7 +100,7 @@ class Contact(PrimaryModel): return reverse('tenancy:contact', args=[self.pk]) -class ContactAssignment(ChangeLoggedModel): +class ContactAssignment(ChangeLoggedModel, TagsMixin): content_type = models.ForeignKey( to=ContentType, on_delete=models.CASCADE diff --git a/netbox/tenancy/tables/contacts.py b/netbox/tenancy/tables/contacts.py index f3b2c871d..2e7525481 100644 --- a/netbox/tenancy/tables/contacts.py +++ b/netbox/tenancy/tables/contacts.py @@ -126,6 +126,9 @@ class ContactAssignmentTable(NetBoxTable): accessor=Accessor('contact__description'), verbose_name=_('Contact Description') ) + tags = columns.TagColumn( + url_name='tenancy:contactassignment_list' + ) actions = columns.ActionsColumn( actions=('edit', 'delete') ) @@ -134,7 +137,7 @@ class ContactAssignmentTable(NetBoxTable): model = ContactAssignment fields = ( 'pk', 'content_type', 'object', 'contact', 'role', 'priority', 'contact_title', 'contact_phone', - 'contact_email', 'contact_address', 'contact_link', 'contact_description', 'actions' + 'contact_email', 'contact_address', 'contact_link', 'contact_description', 'tags', 'actions' ) default_columns = ( 'pk', 'content_type', 'object', 'contact', 'role', 'priority', 'contact_email', 'contact_phone'