Closes #2557: Added object view for tags

This commit is contained in:
Jeremy Stretch 2018-11-15 16:47:41 -05:00
parent 23cde65add
commit 3366a6ae3d
7 changed files with 155 additions and 7 deletions

View File

@ -3,6 +3,7 @@ v2.4.8 (FUTURE)
## Enhancements ## Enhancements
* [#2490](https://github.com/digitalocean/netbox/issues/2490) - Added bulk editing for config contexts * [#2490](https://github.com/digitalocean/netbox/issues/2490) - Added bulk editing for config contexts
* [#2557](https://github.com/digitalocean/netbox/issues/2557) - Added object view for tags
## Bug Fixes ## Bug Fixes

View File

@ -208,6 +208,11 @@ class AddRemoveTagsForm(forms.Form):
self.fields['remove_tags'] = TagField(required=False) self.fields['remove_tags'] = TagField(required=False)
class TagFilterForm(BootstrapMixin, forms.Form):
model = Tag
q = forms.CharField(required=False, label='Search')
# #
# Config contexts # Config contexts
# #

View File

@ -1,7 +1,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import django_tables2 as tables import django_tables2 as tables
from taggit.models import Tag 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, ToggleColumn
from .models import ConfigContext, ObjectChange from .models import ConfigContext, ObjectChange
@ -15,6 +16,14 @@ TAG_ACTIONS = """
{% endif %} {% endif %}
""" """
TAGGED_ITEM = """
{% if value.get_absolute_url %}
<a href="{{ value.get_absolute_url }}">{{ value }}</a>
{% else %}
{{ value }}
{% endif %}
"""
CONFIGCONTEXT_ACTIONS = """ CONFIGCONTEXT_ACTIONS = """
{% if perms.extras.change_configcontext %} {% if perms.extras.change_configcontext %}
<a href="{% url 'extras:configcontext_edit' pk=record.pk %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a> <a href="{% url 'extras:configcontext_edit' pk=record.pk %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
@ -55,6 +64,10 @@ OBJECTCHANGE_REQUEST_ID = """
class TagTable(BaseTable): class TagTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn(
viewname='extras:tag',
args=[Accessor('slug')]
)
actions = tables.TemplateColumn( actions = tables.TemplateColumn(
template_code=TAG_ACTIONS, template_code=TAG_ACTIONS,
attrs={'td': {'class': 'text-right'}}, attrs={'td': {'class': 'text-right'}},
@ -66,6 +79,21 @@ class TagTable(BaseTable):
fields = ('pk', 'name', 'items', 'slug', 'actions') fields = ('pk', 'name', 'items', 'slug', 'actions')
class TaggedItemTable(BaseTable):
content_object = tables.TemplateColumn(
template_code=TAGGED_ITEM,
orderable=False,
verbose_name='Object'
)
content_type = tables.Column(
verbose_name='Type'
)
class Meta(BaseTable.Meta):
model = TaggedItem
fields = ('content_object', 'content_type')
class ConfigContextTable(BaseTable): class ConfigContextTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn() name = tables.LinkColumn()

View File

@ -9,6 +9,7 @@ urlpatterns = [
# Tags # Tags
url(r'^tags/$', views.TagListView.as_view(), name='tag_list'), url(r'^tags/$', views.TagListView.as_view(), name='tag_list'),
url(r'^tags/(?P<slug>[\w-]+)/$', views.TagView.as_view(), name='tag'),
url(r'^tags/(?P<slug>[\w-]+)/edit/$', views.TagEditView.as_view(), name='tag_edit'), url(r'^tags/(?P<slug>[\w-]+)/edit/$', views.TagEditView.as_view(), name='tag_edit'),
url(r'^tags/(?P<slug>[\w-]+)/delete/$', views.TagDeleteView.as_view(), name='tag_delete'), url(r'^tags/(?P<slug>[\w-]+)/delete/$', views.TagDeleteView.as_view(), name='tag_delete'),
url(r'^tags/delete/$', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'), url(r'^tags/delete/$', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'),

View File

@ -1,6 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django import template from django import template
from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -9,18 +10,20 @@ from django.http import Http404
from django.shortcuts import get_object_or_404, redirect, render 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 taggit.models import Tag 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.views import BulkDeleteView, BulkEditView, ObjectDeleteView, ObjectEditView, ObjectListView from utilities.views import BulkDeleteView, BulkEditView, ObjectDeleteView, ObjectEditView, ObjectListView
from . import filters from . import filters
from .forms import ( from .forms import (
ConfigContextForm, ConfigContextBulkEditForm, ConfigContextFilterForm, ImageAttachmentForm, ObjectChangeFilterForm, ConfigContextForm, ConfigContextBulkEditForm, ConfigContextFilterForm, ImageAttachmentForm, ObjectChangeFilterForm,
TagForm, TagFilterForm, TagForm,
) )
from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult
from .reports import get_report, get_reports from .reports import get_report, get_reports
from .tables import ConfigContextTable, ObjectChangeTable, TagTable from .tables import ConfigContextTable, ObjectChangeTable, TagTable, TaggedItemTable
# #
@ -28,11 +31,45 @@ from .tables import ConfigContextTable, ObjectChangeTable, TagTable
# #
class TagListView(ObjectListView): class TagListView(ObjectListView):
queryset = Tag.objects.annotate(items=Count('taggit_taggeditem_items')).order_by('name') queryset = Tag.objects.annotate(
items=Count('taggit_taggeditem_items')
).order_by(
'name'
)
filter = filters.TagFilter
filter_form = TagFilterForm
table = TagTable table = TagTable
template_name = 'extras/tag_list.html' template_name = 'extras/tag_list.html'
class TagView(View):
def get(self, request, slug):
tag = get_object_or_404(Tag, slug=slug)
tagged_items = TaggedItem.objects.filter(
tag=tag
).select_related(
'content_type'
).prefetch_related(
'content_object'
)
# Generate a table of all items tagged with this Tag
items_table = TaggedItemTable(tagged_items)
paginate = {
'klass': EnhancedPaginator,
'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
}
RequestConfig(request, paginate).configure(items_table)
return render(request, 'extras/tag.html', {
'tag': tag,
'items_count': tagged_items.count(),
'items_table': items_table,
})
class TagEditView(PermissionRequiredMixin, ObjectEditView): class TagEditView(PermissionRequiredMixin, ObjectEditView):
permission_required = 'taggit.change_tag' permission_required = 'taggit.change_tag'
model = Tag model = Tag
@ -48,7 +85,11 @@ class TagDeleteView(PermissionRequiredMixin, ObjectDeleteView):
class TagBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class TagBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'circuits.delete_circuittype' permission_required = 'circuits.delete_circuittype'
queryset = Tag.objects.annotate(items=Count('taggit_taggeditem_items')).order_by('name') queryset = Tag.objects.annotate(
items=Count('taggit_taggeditem_items')
).order_by(
'name'
)
table = TagTable table = TagTable
default_return_url = 'extras:tag_list' default_return_url = 'extras:tag_list'

View File

@ -0,0 +1,69 @@
{% extends '_base.html' %}
{% load helpers %}
{% block header %}
<div class="row">
<div class="col-sm-8 col-md-9">
<ol class="breadcrumb">
<li><a href="{% url 'extras:tag_list' %}">Tags</a></li>
<li>{{ tag }}</li>
</ol>
</div>
<div class="col-sm-4 col-md-3">
<form action="{% url 'extras:tag_list' %}" method="get">
<div class="input-group">
<input type="text" name="q" class="form-control" />
<span class="input-group-btn">
<button type="submit" class="btn btn-primary">
<span class="fa fa-search" aria-hidden="true"></span>
</button>
</span>
</div>
</form>
</div>
</div>
<div class="pull-right">
{% if perms.taggit.change_tag %}
<a href="{% url 'extras:tag_edit' slug=tag.slug %}?return_url={% url 'extras:tag' slug=tag.slug %}" class="btn btn-warning">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
Edit this tag
</a>
{% endif %}
</div>
<h1>{% block title %}Tag: {{ tag }}{% endblock %}</h1>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Tag</strong>
</div>
<table class="table table-hover panel-body attr-table">
<tr>
<td>Name</td>
<td>
{{ tag.name }}
</td>
</tr>
<tr>
<td>Slug</td>
<td>
{{ tag.slug }}
</td>
</tr>
<tr>
<td>Tagged Items</td>
<td>
{{ items_count }}
</td>
</tr>
</table>
</div>
</div>
<div class="col-md-6">
{% include 'panel_table.html' with table=items_table heading='Tagged Objects' %}
</div>
</div>
{% endblock %}

View File

@ -4,8 +4,11 @@
{% block content %} {% block content %}
<h1>{% block title %}Tags{% endblock %}</h1> <h1>{% block title %}Tags{% endblock %}</h1>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-9">
{% include 'utilities/obj_table.html' with bulk_delete_url='extras:tag_bulk_delete' %} {% include 'utilities/obj_table.html' with bulk_delete_url='extras:tag_bulk_delete' %}
</div> </div>
<div class="col-md-3">
{% include 'inc/search_panel.html' %}
</div>
</div> </div>
{% endblock %} {% endblock %}