Commit e364191b by Alex Gaynor

Optimize TaggableManager.add, it should do fewer queries now.

parent 9229a50a
......@@ -126,9 +126,21 @@ class _TaggableManager(models.Manager):
@require_instance_manager
def add(self, *tags):
for tag in tags:
if not isinstance(tag, self.through.tag_model()):
tag, _ = self.through.tag_model().objects.get_or_create(name=tag)
str_tags = set([
t
for t in tags
if not isinstance(t, self.through.tag_model())
])
tag_objs = set(tags) - str_tags
existing = self.through.tag_model().objects.filter(
name__in=str_tags
)
tag_objs.update(existing)
for new_tag in str_tags - set(t.name for t in existing):
tag_objs.add(self.through.tag_model().objects.create(name=new_tag))
for tag in tag_objs:
self.through.objects.get_or_create(tag=tag, **self._lookup_kwargs())
@require_instance_manager
......
from unittest import TestCase as UnitTestCase
from django.conf import settings
from django.db import connection
from django.test import TestCase, TransactionTestCase
from taggit.models import Tag, TaggedItem
......@@ -20,6 +22,19 @@ class BaseTaggingTest(object):
tags.sort()
self.assertEqual(got, tags)
def assert_num_queries(self, n, f, *args, **kwargs):
original_DEBUG = settings.DEBUG
settings.DEBUG = True
current = len(connection.queries)
try:
f(*args, **kwargs)
self.assertEqual(
len(connection.queries) - current,
n,
)
finally:
settings.DEBUG = original_DEBUG
class BaseTaggingTestCase(TestCase, BaseTaggingTest):
pass
......@@ -97,6 +112,20 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
apple.delete()
self.assert_tags_equal(self.food_model.tags.all(), ["green"])
def test_add_queries(self):
apple = self.food_model.objects.create(name="apple")
# 1 query to see which tags exist
# + 3 queries to create the tags.
# + 6 queries to create the intermediary things (including SELECTs, to
# make sure we don't double create.
self.assert_num_queries(10, apple.tags.add, "red", "delicious", "green")
pear = self.food_model.objects.create(name="pear")
# 1 query to see which tags exist
# + 4 queries to create the intermeidary things (including SELECTs, to
# make sure we dont't double create.
self.assert_num_queries(5, pear.tags.add, "green", "delicious")
def test_require_pk(self):
food_instance = self.food_model()
self.assertRaises(ValueError, lambda: food_instance.tags.all())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment