diff --git a/CHANGELOG.rst b/CHANGELOG.rst index eefdd7b8..6a3ced30 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,8 @@ Changelog (Unreleased) ~~~~~~~~~~~~ +* Add an admin command to remove orphaned tags + 6.1.0 (2024-09-29) ~~~~~~~~~~~~~~~~~~ diff --git a/taggit/admin.py b/taggit/admin.py index 408a3d42..7d0847bb 100644 --- a/taggit/admin.py +++ b/taggit/admin.py @@ -19,7 +19,7 @@ class TagAdmin(admin.ModelAdmin): ordering = ["name", "slug"] search_fields = ["name"] prepopulated_fields = {"slug": ["name"]} - actions = ["render_tag_form"] + actions = ["render_tag_form", "remove_orphaned_tags_action"] def get_urls(self): urls = super().get_urls() @@ -84,3 +84,14 @@ def merge_tags_view(self, request): "selected_tag_ids": selected_tag_ids, } return render(request, "admin/taggit/merge_tags_form.html", context) + + @admin.action(description="Remove orphaned tags") + def remove_orphaned_tags_action(self, request, queryset): + try: + orphaned_tags = queryset.objects.orphaned() + count, _ = orphaned_tags.delete() + self.message_user( + request, f"Successfully removed {count} orphaned tags.", level="success" + ) + except Exception as e: + self.message_user(request, f"An error occurred: {e}", level="error") diff --git a/taggit/management/commands/remove_orphaned_tags.py b/taggit/management/commands/remove_orphaned_tags.py index 1bd799e7..12e89740 100644 --- a/taggit/management/commands/remove_orphaned_tags.py +++ b/taggit/management/commands/remove_orphaned_tags.py @@ -7,6 +7,6 @@ class Command(BaseCommand): help = "Remove orphaned tags" def handle(self, *args, **options): - orphaned_tags = Tag.objects.filter(taggit_taggeditem_items=None) + orphaned_tags = Tag.objects.orphaned() count = orphaned_tags.delete() self.stdout.write(f"Successfully removed {count} orphaned tags") diff --git a/taggit/models.py b/taggit/models.py index 091d733b..0b14e7d9 100644 --- a/taggit/models.py +++ b/taggit/models.py @@ -105,7 +105,16 @@ def slugify(self, tag, i=None): return slug +class TagQuerySet(models.QuerySet): + + def orphaned(self): + return self.filter(taggit_taggeditem_items=None) + + class Tag(TagBase): + + objects = NaturalKeyManager.from_queryset(TagQuerySet)() + class Meta: verbose_name = _("tag") verbose_name_plural = _("tags")