|
@@ -0,0 +1,67 @@
|
|
|
|
|
+from django.db import router
|
|
|
|
|
+from django.db.models import signals
|
|
|
|
|
+from taggit.managers import _TaggableManager
|
|
|
|
|
+from taggit.utils import require_instance_manager
|
|
|
|
|
+
|
|
|
|
|
+__all__ = (
|
|
|
|
|
+ 'NetBoxTaggableManager',
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class NetBoxTaggableManager(_TaggableManager):
|
|
|
|
|
+ """
|
|
|
|
|
+ Extends taggit's _TaggableManager to replace the per-tag get_or_create loop in add() with a
|
|
|
|
|
+ single bulk_create() call, reducing SQL queries from O(N) to O(1) when assigning tags.
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+ @require_instance_manager
|
|
|
|
|
+ def add(self, *tags, through_defaults=None, tag_kwargs=None, **kwargs):
|
|
|
|
|
+ self._remove_prefetched_objects()
|
|
|
|
|
+ if tag_kwargs is None:
|
|
|
|
|
+ tag_kwargs = {}
|
|
|
|
|
+ db = router.db_for_write(self.through, instance=self.instance)
|
|
|
|
|
+
|
|
|
|
|
+ tag_objs = self._to_tag_model_instances(tags, tag_kwargs)
|
|
|
|
|
+ new_ids = {t.pk for t in tag_objs}
|
|
|
|
|
+
|
|
|
|
|
+ # Determine which tags are not already assigned to this object
|
|
|
|
|
+ lookup = self._lookup_kwargs()
|
|
|
|
|
+ vals = set(
|
|
|
|
|
+ self.through._default_manager.using(db)
|
|
|
|
|
+ .values_list("tag_id", flat=True)
|
|
|
|
|
+ .filter(**lookup, tag_id__in=new_ids)
|
|
|
|
|
+ )
|
|
|
|
|
+ new_ids -= vals
|
|
|
|
|
+
|
|
|
|
|
+ if not new_ids:
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ signals.m2m_changed.send(
|
|
|
|
|
+ sender=self.through,
|
|
|
|
|
+ action="pre_add",
|
|
|
|
|
+ instance=self.instance,
|
|
|
|
|
+ reverse=False,
|
|
|
|
|
+ model=self.through.tag_model(),
|
|
|
|
|
+ pk_set=new_ids,
|
|
|
|
|
+ using=db,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # Use a single bulk INSERT instead of one get_or_create per tag.
|
|
|
|
|
+ self.through._default_manager.using(db).bulk_create(
|
|
|
|
|
+ [
|
|
|
|
|
+ self.through(tag=tag, **lookup, **(through_defaults or {}))
|
|
|
|
|
+ for tag in tag_objs
|
|
|
|
|
+ if tag.pk in new_ids
|
|
|
|
|
+ ],
|
|
|
|
|
+ ignore_conflicts=True,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ signals.m2m_changed.send(
|
|
|
|
|
+ sender=self.through,
|
|
|
|
|
+ action="post_add",
|
|
|
|
|
+ instance=self.instance,
|
|
|
|
|
+ reverse=False,
|
|
|
|
|
+ model=self.through.tag_model(),
|
|
|
|
|
+ pk_set=new_ids,
|
|
|
|
|
+ using=db,
|
|
|
|
|
+ )
|