Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#682] - Remove Orphaned Tags #908

Merged
merged 4 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Changelog

(Unreleased)
~~~~~~~~~~~~
* Added a management command (``remove_orphaned_tags``) to remove orphaned tags

6.0.0 (2024-07-27)
~~~~~~~~~~~~~~~~~~
Expand Down
12 changes: 12 additions & 0 deletions taggit/management/commands/remove_orphaned_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.core.management.base import BaseCommand

from taggit.models import Tag


class Command(BaseCommand):
help = "Remove orphaned tags"

def handle(self, *args, **options):
orphaned_tags = Tag.objects.filter(taggit_taggeditem_items=None)
count = orphaned_tags.delete()
self.stdout.write(f"Successfully removed {count} orphaned tags")
58 changes: 58 additions & 0 deletions tests/test_remove_orphaned_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from django.core.management import call_command
from django.test import TestCase

from taggit.models import Tag
from tests.models import Food, HousePet


class RemoveOrphanedTagsTests(TestCase):
def setUp(self):
# Create some tags, some orphaned and some not
self.orphan_tag1 = Tag.objects.create(name="Orphan1")
self.orphan_tag2 = Tag.objects.create(name="Orphan2")
self.used_tag = Tag.objects.create(name="Used")

# Create instances of Food and HousePet and tag them
self.food_item = Food.objects.create(name="Apple")
self.pet_item = HousePet.objects.create(name="Fido")

self.food_item.tags.add(self.used_tag)
self.pet_item.tags.add(self.used_tag)

def test_remove_orphaned_tags(self):
# Ensure the setup is correct
self.assertEqual(Tag.objects.count(), 3)
self.assertEqual(Tag.objects.filter(taggit_taggeditem_items=None).count(), 2)

# Call the management command to remove orphaned tags
call_command("remove_orphaned_tags")

# Check the count of tags after running the command
self.assertEqual(Tag.objects.count(), 1)
self.assertEqual(Tag.objects.filter(taggit_taggeditem_items=None).count(), 0)

# Ensure that the used tag still exists
self.assertTrue(Tag.objects.filter(name="Used").exists())
self.assertFalse(Tag.objects.filter(name="Orphan1").exists())
self.assertFalse(Tag.objects.filter(name="Orphan2").exists())

def test_no_orphaned_tags(self):
# Ensure the setup is correct
self.assertEqual(Tag.objects.count(), 3)
self.assertEqual(Tag.objects.filter(taggit_taggeditem_items=None).count(), 2)

# Add used_tag to food_item to make no tags orphaned
self.food_item.tags.add(self.orphan_tag1)
self.food_item.tags.add(self.orphan_tag2)

# Call the management command to remove orphaned tags
call_command("remove_orphaned_tags")

# Check the count of tags after running the command
self.assertEqual(Tag.objects.count(), 3)
self.assertEqual(Tag.objects.filter(taggit_taggeditem_items=None).count(), 0)

# Ensure all tags still exist
self.assertTrue(Tag.objects.filter(name="Used").exists())
self.assertTrue(Tag.objects.filter(name="Orphan1").exists())
self.assertTrue(Tag.objects.filter(name="Orphan2").exists())
Loading