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
* [#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

View File

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

View File

@ -1,7 +1,8 @@
from __future__ import unicode_literals
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 .models import ConfigContext, ObjectChange
@ -15,6 +16,14 @@ TAG_ACTIONS = """
{% endif %}
"""
TAGGED_ITEM = """
{% if value.get_absolute_url %}
<a href="{{ value.get_absolute_url }}">{{ value }}</a>
{% else %}
{{ value }}
{% endif %}
"""
CONFIGCONTEXT_ACTIONS = """
{% 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>
@ -55,6 +64,10 @@ OBJECTCHANGE_REQUEST_ID = """
class TagTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn(
viewname='extras:tag',
args=[Accessor('slug')]
)
actions = tables.TemplateColumn(
template_code=TAG_ACTIONS,
attrs={'td': {'class': 'text-right'}},
@ -66,6 +79,21 @@ class TagTable(BaseTable):
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):
pk = ToggleColumn()
name = tables.LinkColumn()

View File

@ -9,6 +9,7 @@ urlpatterns = [
# Tags
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-]+)/delete/$', views.TagDeleteView.as_view(), name='tag_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 django import template
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
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.utils.safestring import mark_safe
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.paginator import EnhancedPaginator
from utilities.views import BulkDeleteView, BulkEditView, ObjectDeleteView, ObjectEditView, ObjectListView
from . import filters
from .forms import (
ConfigContextForm, ConfigContextBulkEditForm, ConfigContextFilterForm, ImageAttachmentForm, ObjectChangeFilterForm,
TagForm,
TagFilterForm, TagForm,
)
from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult
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):
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
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):
permission_required = 'taggit.change_tag'
model = Tag
@ -48,7 +85,11 @@ class TagDeleteView(PermissionRequiredMixin, ObjectDeleteView):
class TagBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
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
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 %}
<h1>{% block title %}Tags{% endblock %}</h1>
<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' %}
</div>
<div class="col-md-3">
{% include 'inc/search_panel.html' %}
</div>
</div>
{% endblock %}