2
0

managers.py 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. from django.db import router
  2. from django.db.models import signals
  3. from taggit.managers import _TaggableManager
  4. from taggit.utils import require_instance_manager
  5. __all__ = (
  6. 'NetBoxTaggableManager',
  7. )
  8. class NetBoxTaggableManager(_TaggableManager):
  9. """
  10. Extends taggit's _TaggableManager to replace the per-tag get_or_create loop in add() with a
  11. single bulk_create() call, reducing SQL queries from O(N) to O(1) when assigning tags.
  12. """
  13. @require_instance_manager
  14. def add(self, *tags, through_defaults=None, tag_kwargs=None, **kwargs):
  15. self._remove_prefetched_objects()
  16. if tag_kwargs is None:
  17. tag_kwargs = {}
  18. db = router.db_for_write(self.through, instance=self.instance)
  19. tag_objs = self._to_tag_model_instances(tags, tag_kwargs)
  20. new_ids = {t.pk for t in tag_objs}
  21. # Determine which tags are not already assigned to this object
  22. lookup = self._lookup_kwargs()
  23. vals = set(
  24. self.through._default_manager.using(db)
  25. .values_list("tag_id", flat=True)
  26. .filter(**lookup, tag_id__in=new_ids)
  27. )
  28. new_ids -= vals
  29. if not new_ids:
  30. return
  31. signals.m2m_changed.send(
  32. sender=self.through,
  33. action="pre_add",
  34. instance=self.instance,
  35. reverse=False,
  36. model=self.through.tag_model(),
  37. pk_set=new_ids,
  38. using=db,
  39. )
  40. # Use a single bulk INSERT instead of one get_or_create per tag.
  41. self.through._default_manager.using(db).bulk_create(
  42. [
  43. self.through(tag=tag, **lookup, **(through_defaults or {}))
  44. for tag in tag_objs
  45. if tag.pk in new_ids
  46. ],
  47. ignore_conflicts=True,
  48. )
  49. signals.m2m_changed.send(
  50. sender=self.through,
  51. action="post_add",
  52. instance=self.instance,
  53. reverse=False,
  54. model=self.through.tag_model(),
  55. pk_set=new_ids,
  56. using=db,
  57. )