mirror of
https://github.com/netbox-community/netbox.git
synced 2026-03-05 22:10:07 +01:00
Compare commits
2 Commits
feature
...
21330-tags
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a787c86b6c | ||
|
|
0ea353eed3 |
65
netbox/extras/managers.py
Normal file
65
netbox/extras/managers.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from django.db import router
|
||||
from django.db.models import signals
|
||||
from taggit.managers import _TaggableManager
|
||||
|
||||
__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.
|
||||
"""
|
||||
|
||||
def add(self, *tags, through_defaults=None, tag_kwargs=None, **kwargs):
|
||||
self._remove_prefetched_objects()
|
||||
if tag_kwargs is None:
|
||||
tag_kwargs = {}
|
||||
|
||||
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
|
||||
db = router.db_for_write(self.through, instance=self.instance)
|
||||
vals = set(
|
||||
self.through._default_manager.using(db)
|
||||
.values_list("tag_id", flat=True)
|
||||
.filter(**self._lookup_kwargs())
|
||||
)
|
||||
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.
|
||||
lookup = self._lookup_kwargs()
|
||||
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,
|
||||
)
|
||||
@@ -53,8 +53,11 @@ class TaggableModelSerializer(serializers.Serializer):
|
||||
|
||||
def _save_tags(self, instance, tags):
|
||||
if tags:
|
||||
# Cache tags on instance so serialize_object() can reuse them without a DB query
|
||||
instance._tags = tags
|
||||
instance.tags.set([t.name for t in tags])
|
||||
else:
|
||||
instance._tags = []
|
||||
instance.tags.clear()
|
||||
|
||||
return instance
|
||||
|
||||
@@ -15,6 +15,7 @@ from core.choices import JobStatusChoices, ObjectChangeActionChoices
|
||||
from core.models import ObjectType
|
||||
from extras.choices import *
|
||||
from extras.constants import CUSTOMFIELD_EMPTY_VALUES
|
||||
from extras.managers import NetBoxTaggableManager
|
||||
from extras.utils import is_taggable
|
||||
from netbox.config import get_config
|
||||
from netbox.constants import CORE_APPS
|
||||
@@ -487,11 +488,12 @@ class JournalingMixin(models.Model):
|
||||
class TagsMixin(models.Model):
|
||||
"""
|
||||
Enables support for tag assignment. Assigned tags can be managed via the `tags` attribute,
|
||||
which is a `TaggableManager` instance.
|
||||
which is a `NetBoxTaggableManager` instance.
|
||||
"""
|
||||
tags = TaggableManager(
|
||||
through='extras.TaggedItem',
|
||||
ordering=('weight', 'name'),
|
||||
manager=NetBoxTaggableManager,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
Reference in New Issue
Block a user