Standardize add, import, and export functionality for tags

This commit is contained in:
Jeremy Stretch 2020-06-12 09:48:23 -04:00
parent 057a022205
commit da906f48d9
7 changed files with 58 additions and 25 deletions

View File

@ -1,6 +1,7 @@
from django import forms
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.utils.safestring import mark_safe
from mptt.forms import TreeNodeMultipleChoiceField
from taggit.forms import TagField as TagField_
@ -161,6 +162,17 @@ class TagForm(BootstrapMixin, forms.ModelForm):
]
class TagCSVForm(CSVModelForm):
slug = SlugField()
class Meta:
model = Tag
fields = Tag.csv_headers
help_texts = {
'color': mark_safe('RGB color in hexadecimal (e.g. <code>00ff00</code>)'),
}
class AddRemoveTagsForm(forms.Form):
def __init__(self, *args, **kwargs):

View File

@ -24,6 +24,8 @@ class Tag(TagBase, ChangeLoggedModel):
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'color', 'description']
def get_absolute_url(self):
return reverse('extras:tag', args=[self.slug])
@ -34,6 +36,14 @@ class Tag(TagBase, ChangeLoggedModel):
slug += "_%d" % i
return slug
def to_csv(self):
return (
self.name,
self.slug,
self.color,
self.description
)
class TaggedItem(GenericTaggedItemBase):
tag = models.ForeignKey(

View File

@ -10,16 +10,7 @@ from extras.models import ConfigContext, ObjectChange, Tag
from utilities.testing import ViewTestCases, TestCase
# TODO: Change base class to PrimaryObjectViewTestCase
# Blocked by #3703
class TagTestCase(
ViewTestCases.GetObjectViewTestCase,
ViewTestCases.EditObjectViewTestCase,
ViewTestCases.DeleteObjectViewTestCase,
ViewTestCases.ListObjectsViewTestCase,
ViewTestCases.BulkEditObjectsViewTestCase,
ViewTestCases.BulkDeleteObjectsViewTestCase
):
class TagTestCase(ViewTestCases.PrimaryObjectViewTestCase):
model = Tag
@classmethod
@ -38,6 +29,13 @@ class TagTestCase(
'comments': 'Some comments',
}
cls.csv_data = (
"name,slug,color,description",
"Tag 4,tag-4,ff0000,Fourth tag",
"Tag 5,tag-5,00ff00,Fifth tag",
"Tag 6,tag-6,0000ff,Sixth tag",
)
cls.bulk_edit_data = {
'color': '00ff00',
}

View File

@ -9,6 +9,8 @@ urlpatterns = [
# Tags
path('tags/', views.TagListView.as_view(), name='tag_list'),
path('tags/add/', views.TagEditView.as_view(), name='tag_add'),
path('tags/import/', views.TagBulkImportView.as_view(), name='tag_import'),
path('tags/edit/', views.TagBulkEditView.as_view(), name='tag_bulk_edit'),
path('tags/delete/', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'),
path('tags/<str:slug>/', views.TagView.as_view(), name='tag'),

View File

@ -13,14 +13,13 @@ from utilities.forms import ConfirmationForm
from utilities.paginator import EnhancedPaginator
from utilities.utils import shallow_compare_dict
from utilities.views import (
BulkDeleteView, BulkEditView, ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView,
BulkDeleteView, BulkEditView, BulkImportView, ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView,
ObjectPermissionRequiredMixin,
)
from . import filters, forms
from . import filters, forms, tables
from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult, Tag, TaggedItem
from .reports import get_report, get_reports
from .scripts import get_scripts, run_script
from .tables import ConfigContextTable, ObjectChangeTable, TagTable, TaggedItemTable
#
@ -35,8 +34,7 @@ class TagListView(ObjectListView):
)
filterset = filters.TagFilterSet
filterset_form = forms.TagFilterForm
table = TagTable
action_buttons = ()
table = tables.TagTable
class TagView(ObjectView):
@ -52,7 +50,7 @@ class TagView(ObjectView):
)
# Generate a table of all items tagged with this Tag
items_table = TaggedItemTable(tagged_items)
items_table = tables.TaggedItemTable(tagged_items)
paginate = {
'paginator_class': EnhancedPaginator,
'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
@ -78,13 +76,20 @@ class TagDeleteView(ObjectDeleteView):
default_return_url = 'extras:tag_list'
class TagBulkImportView(BulkImportView):
queryset = Tag.objects.all()
model_form = forms.TagCSVForm
table = tables.TagTable
default_return_url = 'extras:tag_list'
class TagBulkEditView(BulkEditView):
queryset = Tag.objects.annotate(
items=Count('extras_taggeditem_items', distinct=True)
).order_by(
'name'
)
table = TagTable
table = tables.TagTable
form = forms.TagBulkEditForm
default_return_url = 'extras:tag_list'
@ -95,7 +100,7 @@ class TagBulkDeleteView(BulkDeleteView):
).order_by(
'name'
)
table = TagTable
table = tables.TagTable
default_return_url = 'extras:tag_list'
@ -107,7 +112,7 @@ class ConfigContextListView(ObjectListView):
queryset = ConfigContext.objects.all()
filterset = filters.ConfigContextFilterSet
filterset_form = forms.ConfigContextFilterForm
table = ConfigContextTable
table = tables.ConfigContextTable
action_buttons = ('add',)
@ -143,7 +148,7 @@ class ConfigContextEditView(ObjectEditView):
class ConfigContextBulkEditView(BulkEditView):
queryset = ConfigContext.objects.all()
filterset = filters.ConfigContextFilterSet
table = ConfigContextTable
table = tables.ConfigContextTable
form = forms.ConfigContextBulkEditForm
default_return_url = 'extras:configcontext_list'
@ -155,7 +160,7 @@ class ConfigContextDeleteView(ObjectDeleteView):
class ConfigContextBulkDeleteView(BulkDeleteView):
queryset = ConfigContext.objects.all()
table = ConfigContextTable
table = tables.ConfigContextTable
default_return_url = 'extras:configcontext_list'
@ -197,7 +202,7 @@ class ObjectChangeListView(ObjectListView):
queryset = ObjectChange.objects.prefetch_related('user', 'changed_object_type')
filterset = filters.ObjectChangeFilterSet
filterset_form = forms.ObjectChangeFilterForm
table = ObjectChangeTable
table = tables.ObjectChangeTable
template_name = 'extras/objectchange_list.html'
action_buttons = ('export',)
@ -214,7 +219,7 @@ class ObjectChangeView(ObjectView):
).exclude(
pk=objectchange.pk
)
related_changes_table = ObjectChangeTable(
related_changes_table = tables.ObjectChangeTable(
data=related_changes[:50],
orderable=False
)
@ -267,7 +272,7 @@ class ObjectChangeLogView(View):
Q(changed_object_type=content_type, changed_object_id=obj.pk) |
Q(related_object_type=content_type, related_object_id=obj.pk)
)
objectchanges_table = ObjectChangeTable(
objectchanges_table = tables.ObjectChangeTable(
data=objectchanges,
orderable=False
)

View File

@ -85,7 +85,7 @@
<tr>
<td>Description</td>
<td>
{{ tag.description }}
{{ tag.description|placeholder }}
</td>
</table>
</div>

View File

@ -101,6 +101,12 @@
<li class="divider"></li>
<li class="dropdown-header">Tags</li>
<li{% if not perms.extras.view_tag %} class="disabled"{% endif %}>
{% if perms.extras.add_tag %}
<div class="buttons pull-right">
<a href="{% url 'extras:tag_add' %}" class="btn btn-xs btn-success" title="Add"><i class="fa fa-plus"></i></a>
<a href="{% url 'extras:tag_import' %}" class="btn btn-xs btn-info" title="Import"><i class="fa fa-download"></i></a>
</div>
{% endif %}
<a href="{% url 'extras:tag_list' %}">Tags</a>
</li>
</ul>