alex/django-taggit — pull request #270 by ivancrneto (2014-09-26 01:33:10+00:00)
Title: handle adding tags using through when table is case-insensitive

I have the following:

```Python
class CampaignFeature(TaggedItemBase):
content_object = models.ForeignKey('Campaign')
order = models.IntegerField(blank=True, null=True)

class Meta:
ordering = ('order',)

class Campaign(models.Model):
features = TaggableManager(
verbose_name='Campaign features',
blank=True,
through=CampaignFeature)
```

When I create a ```Campaign``` object in admin, and use for example ```"Smartphone app"``` as one of the tags, then I create another using ```"Smartphone App"```, I get an IntegrityError ```(1062, "Duplicate entry '' for key 'name'")```

As my MySQL table columns are case-insensitive, an interesting behaviour happens with taggit:

https://github.com/ivancrneto/django-taggit/blob/develop/taggit/managers.py#L154
```Python
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))
```
The error is raised in this instruction. Right before this, we have:

```Python
str_tags == set([u'Smartphone App']) # we are trying to add 'Smartphone App' in the second creation
# [...]
existing = self.through.tag_model().objects.filter(
name__in=str_tags
)
# query: SELECT `taggit_tag`.`id`, `taggit_tag`.`name`, `taggit_tag`.`slug` FROM `taggit_tag` WHERE `taggit_tag`.`name` IN ("Smartphone App");

existing == []
```
The first tag we added is returned by the query because the table is case-insensitive.

```Python
set(t.name for t in existing) == set([u'Smartphone app'])
str_tags - set(t.name for t in existing) == set([u'Smartphone App'])
```
So it tries to add "Smartphone App" and the db raises ERROR 1062 (23000): Duplicate entry 'Smartphone App' for key 'name' because there is already a 'Smartphone app' in a case-insensitive column.

Checking for the exception and just passing if the IntegrityError is related with 'Duplicate entry' is enough because ```tag_objs``` already has the existing tag, as in the line 152 we have:
```Python
tag_objs.update(existing)
```