[Python-modules-commits] [django-taggit] 02/09: Imported Upstream version 0.17.0

Michal Cihar nijel at moszumanska.debian.org
Mon Aug 24 12:11:33 UTC 2015


This is an automated email from the git hooks/post-receive script.

nijel pushed a commit to branch master
in repository django-taggit.

commit 53230aa7b606b669104e0b04039643e5ba2c2d08
Author: Michal Čihař <nijel at debian.org>
Date:   Mon Aug 24 13:45:24 2015 +0200

    Imported Upstream version 0.17.0
---
 CHANGELOG.txt                                |  79 +++++--
 LICENSE                                      |  15 +-
 PKG-INFO                                     |  12 +-
 README.rst                                   |  10 +-
 django_taggit.egg-info/PKG-INFO              |  12 +-
 django_taggit.egg-info/SOURCES.txt           |   5 +-
 docs/admin.txt                               |   2 +-
 docs/api.txt                                 |   8 +-
 docs/external_apps.txt                       |   8 +
 docs/forms.txt                               |   2 +-
 docs/getting_started.txt                     |   6 +
 runtests.py                                  |   1 -
 setup.cfg                                    |   1 +
 setup.py                                     |   9 +-
 taggit/__init__.py                           |   2 +-
 taggit/locale/he/LC_MESSAGES/django.po       |   1 -
 taggit/locale/nl/LC_MESSAGES/django.po       |   1 -
 taggit/locale/ru/LC_MESSAGES/django.po       |   1 -
 taggit/managers.py                           | 144 +++++++++----
 taggit/migrations/0001_initial.py            |  14 +-
 taggit/migrations/0002_auto_20150616_2121.py |  18 ++
 taggit/models.py                             |  40 +++-
 taggit/utils.py                              |   8 +
 tests/__init__.pyc                           | Bin 140 -> 144 bytes
 tests/forms.pyc                              | Bin 2110 -> 2548 bytes
 tests/migrations/0001_initial.py             | 294 +++++++++++----------------
 tests/migrations/0001_initial.pyc            | Bin 0 -> 8258 bytes
 tests/migrations/__init__.pyc                | Bin 0 -> 155 bytes
 tests/models.pyc                             | Bin 10529 -> 13839 bytes
 tests/tests.py                               |  32 ++-
 tests/tests.pyc                              | Bin 27788 -> 35511 bytes
 tox.ini                                      |  28 ++-
 32 files changed, 469 insertions(+), 284 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 7760cbb..a3e6ae8 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,16 +1,69 @@
 Changelog
 =========
 
-0.12.2 (21.09.2014)
+0.17.0 (2015-08-14)
+~~~~~~~~~~~~~~~~~~~
+ * Database index added on TaggedItem fields content_type & object_id
+  * https://github.com/alex/django-taggit/pull/319
+
+0.16.4 (2015-08-13)
+~~~~~~~~~~~~~~~~~~~
+ * Access default manager via class instead of instance
+  * https://github.com/alex/django-taggit/pull/335
+
+0.16.3 (2015-08-08)
+~~~~~~~~~~~~~~~~~~~
+ * Prevent IntegrityError with custom TagBase classes
+  * https://github.com/alex/django-taggit/pull/334
+
+0.16.2 (2015-07-13)
+~~~~~~~~~~~~~~~~~~~
+ * Fix an admin bug related to the `Manager` property `through_fields`
+  * https://github.com/alex/django-taggit/pull/328
+
+0.16.1 (2015-07-09)
+~~~~~~~~~~~~~~~~~~~
+ * Fix bug that assumed all primary keys are named 'id'
+  * https://github.com/alex/django-taggit/pull/322
+
+0.16.0 (2015-07-04)
+~~~~~~~~~~~~~~~~~~~
+ * Add option to allow case-insensitive tags
+  * https://github.com/alex/django-taggit/pull/325
+
+0.15.0 (2015-06-23)
+~~~~~~~~~~~~~~~~~~~
+ * Fix wrong slugs for non-latin chars
+  * Only works if optional GPL dependency (unidecode) is installed
+  * https://github.com/alex/django-taggit/pull/315
+  * https://github.com/alex/django-taggit/pull/273
+
+0.14.0 (2015-04-26)
+~~~~~~~~~~~~~~~~~~~
+ * Prevent extra JOIN when prefetching
+  * https://github.com/alex/django-taggit/pull/275
+ * Prevent _meta warnings with Django 1.8
+  * https://github.com/alex/django-taggit/pull/299
+
+0.13.0 (2015-04-02)
+~~~~~~~~~~~~~~~~~~~
+ * Django 1.8 support
+  * https://github.com/alex/django-taggit/pull/297
+
+0.12.3 (2015-03-03)
+~~~~~~~~~~~~~~~~~~~
+ * Specify that the internal type of the TaggitManager is a ManyToManyField
+
+0.12.2 (2014-21-09)
 ~~~~~~~~~~~~~~~~~~~
  * Fixed 1.7 migrations.
 
-0.12.1 (10.08.2014)
+0.12.1 (2014-10-08)
 ~~~~~~~~~~~~~~~~~~~
  * Final (hopefully) fixes for the upcoming Django 1.7 release.
  * Added Japanese translation.
 
-0.12.0 (20.04.2014)
+0.12.0 (2014-20-04)
 ~~~~~~~~~~~~~~~~~~~
  * **Backwards incompatible:** Support for Django 1.7 migrations. South users
    have to set ``SOUTH_MIGRATION_MODULES`` to use ``taggit.south_migrations``
@@ -23,15 +76,15 @@ Changelog
    raised by the database, your app will have to handle that.
  * Added Italian and Esperanto translations.
 
-0.11.2 (13.12.2013)
+0.11.2 (2013-13-12)
 ~~~~~~~~~~~~~~~~~~~
  * Forbid multiple TaggableManagers via generic foreign keys.
 
-0.11.1 (25.11.2013)
+0.11.1 (2013-25-11)
 ~~~~~~~~~~~~~~~~~~~
  * Fixed support for Django 1.4 and 1.5.
 
-0.11.0 (25.11.2013)
+0.11.0 (2013-25-11)
 ~~~~~~~~~~~~~~~~~~~
  * Added support for prefetch_related on tags fields.
  * Fixed support for Django 1.7.
@@ -39,7 +92,7 @@ Changelog
  * Allow more than one TaggableManager on models (assuming concrete FKs are
    used for the relations).
 
-0.10.0 (17.08.2013)
+0.10.0 (2013-17-08)
 ~~~~~~~~~~~~~~~~~~~
 
  * Support for Django 1.6 and 1.7.
@@ -47,16 +100,16 @@ Changelog
  * **Backwards incompatible:** Dropped support for Django < 1.4.5.
  * Tag names are unique now, use the provided South migrations to upgrade.
 
-0.9.2
-~~~~~
+0.9.2 (2011-01-17)
+~~~~~~~~~~~~~~~~~~
 
  * **Backwards incompatible:**  Forms containing a :class:`TaggableManager` by
    default now require tags, to change this provide ``blank=True`` to the
    :class:`TaggableManager`.
  * Now works with Django 1.3 (as of beta-1).
 
-0.9.0
-~~~~~
+0.9.0 (2010-09-22)
+~~~~~~~~~~~~~~~~~~
 
  * Added a Hebrew locale.
  * Added an index on the ``object_id`` field of ``TaggedItem``.
@@ -71,8 +124,8 @@ Changelog
  * Removed ``taggit.contrib.suggest``, it now lives in an external application,
    see :doc:`external_apps` for more information.
 
-0.8.0
-~~~~~
+0.8.0 (2010-06-22)
+~~~~~~~~~~~~~~~~~~
 
  * Fixed querying for objects using ``exclude(tags__in=tags)``.
  * Marked strings as translatable.
diff --git a/LICENSE b/LICENSE
index 1231bbe..d2b0157 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,20 +1,20 @@
 Copyright (c) Alex Gaynor and individual contributors.
 All rights reserved.
- 
+
 Redistribution and use in source and binary forms, with or without modification,
 are permitted provided that the following conditions are met:
- 
-    1. Redistributions of source code must retain the above copyright notice, 
+
+    1. Redistributions of source code must retain the above copyright notice,
        this list of conditions and the following disclaimer.
-    
-    2. Redistributions in binary form must reproduce the above copyright 
+
+    2. Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
- 
+
     3. Neither the name of django-taggit nor the names of its contributors
        may be used to endorse or promote products derived from this software
        without specific prior written permission.
- 
+
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -25,4 +25,3 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
diff --git a/PKG-INFO b/PKG-INFO
index b672158..2139e06 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: django-taggit
-Version: 0.12.2
+Version: 0.17.0
 Summary: django-taggit is a reusable Django application for simple tagging.
 Home-page: http://github.com/alex/django-taggit/tree/master
 Author: Alex Gaynor
@@ -10,7 +10,9 @@ Description: django-taggit
         =============
         
         ``django-taggit`` a simpler approach to tagging with Django.  Add ``"taggit"`` to your
-        ``INSTALLED_APPS`` then just add a TaggableManager to your model and go::
+        ``INSTALLED_APPS`` then just add a TaggableManager to your model and go:
+        
+        .. code:: python
         
             from django.db import models
         
@@ -22,7 +24,9 @@ Description: django-taggit
                 tags = TaggableManager()
         
         
-        Then you can use the API like so::
+        Then you can use the API like so:
+        
+        .. code:: python
         
             >>> apple = Food.objects.create(name="apple")
             >>> apple.tags.add("red", "green", "delicious")
@@ -38,7 +42,7 @@ Description: django-taggit
         
         ``django-taggit`` requires Django 1.4.5 or greater.
         
-        For more info check out the documentation.  And for questions about usage or
+        For more info check out the `documentation <https://django-taggit.readthedocs.org/en/latest/>`_.  And for questions about usage or
         development you can contact the
         `mailinglist <http://groups.google.com/group/django-taggit>`_.
         
diff --git a/README.rst b/README.rst
index 735ad5c..c70f655 100644
--- a/README.rst
+++ b/README.rst
@@ -2,7 +2,9 @@ django-taggit
 =============
 
 ``django-taggit`` a simpler approach to tagging with Django.  Add ``"taggit"`` to your
-``INSTALLED_APPS`` then just add a TaggableManager to your model and go::
+``INSTALLED_APPS`` then just add a TaggableManager to your model and go:
+
+.. code:: python
 
     from django.db import models
 
@@ -14,7 +16,9 @@ django-taggit
         tags = TaggableManager()
 
 
-Then you can use the API like so::
+Then you can use the API like so:
+
+.. code:: python
 
     >>> apple = Food.objects.create(name="apple")
     >>> apple.tags.add("red", "green", "delicious")
@@ -30,6 +34,6 @@ Tags will show up for you automatically in forms and the admin.
 
 ``django-taggit`` requires Django 1.4.5 or greater.
 
-For more info check out the documentation.  And for questions about usage or
+For more info check out the `documentation <https://django-taggit.readthedocs.org/en/latest/>`_.  And for questions about usage or
 development you can contact the
 `mailinglist <http://groups.google.com/group/django-taggit>`_.
diff --git a/django_taggit.egg-info/PKG-INFO b/django_taggit.egg-info/PKG-INFO
index b672158..2139e06 100644
--- a/django_taggit.egg-info/PKG-INFO
+++ b/django_taggit.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: django-taggit
-Version: 0.12.2
+Version: 0.17.0
 Summary: django-taggit is a reusable Django application for simple tagging.
 Home-page: http://github.com/alex/django-taggit/tree/master
 Author: Alex Gaynor
@@ -10,7 +10,9 @@ Description: django-taggit
         =============
         
         ``django-taggit`` a simpler approach to tagging with Django.  Add ``"taggit"`` to your
-        ``INSTALLED_APPS`` then just add a TaggableManager to your model and go::
+        ``INSTALLED_APPS`` then just add a TaggableManager to your model and go:
+        
+        .. code:: python
         
             from django.db import models
         
@@ -22,7 +24,9 @@ Description: django-taggit
                 tags = TaggableManager()
         
         
-        Then you can use the API like so::
+        Then you can use the API like so:
+        
+        .. code:: python
         
             >>> apple = Food.objects.create(name="apple")
             >>> apple.tags.add("red", "green", "delicious")
@@ -38,7 +42,7 @@ Description: django-taggit
         
         ``django-taggit`` requires Django 1.4.5 or greater.
         
-        For more info check out the documentation.  And for questions about usage or
+        For more info check out the `documentation <https://django-taggit.readthedocs.org/en/latest/>`_.  And for questions about usage or
         development you can contact the
         `mailinglist <http://groups.google.com/group/django-taggit>`_.
         
diff --git a/django_taggit.egg-info/SOURCES.txt b/django_taggit.egg-info/SOURCES.txt
index 4fdfc77..359f444 100644
--- a/django_taggit.egg-info/SOURCES.txt
+++ b/django_taggit.egg-info/SOURCES.txt
@@ -51,6 +51,7 @@ taggit/locale/pt_BR/LC_MESSAGES/django.po
 taggit/locale/ru/LC_MESSAGES/django.mo
 taggit/locale/ru/LC_MESSAGES/django.po
 taggit/migrations/0001_initial.py
+taggit/migrations/0002_auto_20150616_2121.py
 taggit/migrations/__init__.py
 taggit/south_migrations/0001_initial.py
 taggit/south_migrations/0002_unique_tagnames.py
@@ -64,4 +65,6 @@ tests/models.pyc
 tests/tests.py
 tests/tests.pyc
 tests/migrations/0001_initial.py
-tests/migrations/__init__.py
\ No newline at end of file
+tests/migrations/0001_initial.pyc
+tests/migrations/__init__.py
+tests/migrations/__init__.pyc
\ No newline at end of file
diff --git a/docs/admin.txt b/docs/admin.txt
index b9f7797..fd6917a 100644
--- a/docs/admin.txt
+++ b/docs/admin.txt
@@ -6,7 +6,7 @@ in the admin, just as it will in any other form.  One important thing to note
 is that you *cannot* include a :class:`TaggableManager` in
 :attr:`ModelAdmin.list_display`, if you do you'll see an exception that looks
 like::
-    
+
     AttributeError: 'TaggableManager' object has no attribute 'flatchoices'
 
 This is for the same reason that you cannot include a :class:`ManyToManyField`,
diff --git a/docs/api.txt b/docs/api.txt
index d18ca4a..ff6b7c1 100644
--- a/docs/api.txt
+++ b/docs/api.txt
@@ -55,7 +55,7 @@ playing around with the API.
         same model will be returned.
 
     .. method:: names()
-    
+
         Convenience method, returning a ``ValuesListQuerySet`` (basically
         just an iterable) containing the name of each tag as a string::
 
@@ -69,11 +69,11 @@ playing around with the API.
 
             >>> apple.tags.slugs()
             [u'green-and-juicy', u'red']
-    
+
     .. hint::
 
-       You can subclass ``_TaggableManager`` (note the underscore) to add 
-       methods or functionality. ``TaggableManager`` takes an optional 
+       You can subclass ``_TaggableManager`` (note the underscore) to add
+       methods or functionality. ``TaggableManager`` takes an optional
        manager keyword argument for your custom class, like this::
 
           class Food(models.Model):
diff --git a/docs/external_apps.txt b/docs/external_apps.txt
index 617be0f..1d29d53 100644
--- a/docs/external_apps.txt
+++ b/docs/external_apps.txt
@@ -20,7 +20,15 @@ If you have an application that you'd like to see listed here, simply fork
  * ``django-taggit-templatetags``: Provides several templatetags, including one
    for tag clouds, to expose various ``taggit`` APIs directly to templates.
    Available on `github`__.
+ * ``django-taggit-helpers``: Makes it easier to work with admin pages of models
+   associated with ``taggit`` tags by adding helper classes: ``TaggitCounter``,
+   ``TaggitListFilter``, ``TaggitStackedInline``, ``TaggitTabularInline``.
+   Available on `github`__.
+ * ``django-taggit-labels``: Provides a clickable label widget for the
+   Django admin for user friendly selection from managed tag sets.
 
 __ http://github.com/alex/django-taggit
 __ http://github.com/frankwiles/django-taggit-suggest
 __ http://github.com/feuervogel/django-taggit-templatetags
+__ http://github.com/mfcovington/django-taggit-helpers
+__ https://github.com/bennylope/django-taggit-labels
diff --git a/docs/forms.txt b/docs/forms.txt
index 5d176a0..f364c13 100644
--- a/docs/forms.txt
+++ b/docs/forms.txt
@@ -40,7 +40,7 @@ apple "ball cat" dog   ``["apple", "ball cat", "dog"]``  No commas, so space del
 If, when saving a form, you use the ``commit=False`` option you'll need to call
 ``save_m2m()`` on the form after you save the object, just as you would for a
 form with normal many to many fields on it::
-    
+
     if request.method == "POST":
         form = MyFormClass(request.POST)
         if form.is_valid():
diff --git a/docs/getting_started.txt b/docs/getting_started.txt
index d7423e1..b7903cb 100644
--- a/docs/getting_started.txt
+++ b/docs/getting_started.txt
@@ -31,3 +31,9 @@ And then to any model you want tagging on do the following::
 
         tags = TaggableManager()
 
+.. note::
+
+    If you want ``django-taggit`` to be **CASE INSENSITIVE** when looking up existing tags, you'll have to set to ``True`` the TAGGIT_CASE_INSENSITIVE setting (by default ``False``)::
+
+      TAGGIT_CASE_INSENSITIVE = True
+
diff --git a/runtests.py b/runtests.py
index 55783ee..517dd80 100755
--- a/runtests.py
+++ b/runtests.py
@@ -29,4 +29,3 @@ def runtests():
 
 if __name__ == '__main__':
     runtests()
-
diff --git a/setup.cfg b/setup.cfg
index fafd3e2..dd960e1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,6 +6,7 @@ universal = 1
 
 [flake8]
 ignore = E501,E302
+exclude = south_migrations,migrations
 
 [isort]
 forced_separate = tests,taggit
diff --git a/setup.py b/setup.py
index f994789..cb953af 100644
--- a/setup.py
+++ b/setup.py
@@ -1,13 +1,14 @@
 from setuptools import setup, find_packages
 
+import taggit
 
-f = open('README.rst')
-readme = f.read()
-f.close()
+
+with open('README.rst') as f:
+    readme = f.read()
 
 setup(
     name='django-taggit',
-    version='0.12.2',
+    version='.'.join(str(i) for i in taggit.VERSION),
     description='django-taggit is a reusable Django application for simple tagging.',
     long_description=readme,
     author='Alex Gaynor',
diff --git a/taggit/__init__.py b/taggit/__init__.py
index 00f81bf..8e148b4 100644
--- a/taggit/__init__.py
+++ b/taggit/__init__.py
@@ -1 +1 @@
-VERSION = (0, 12, 2)
+VERSION = (0, 17, 0)
diff --git a/taggit/locale/he/LC_MESSAGES/django.po b/taggit/locale/he/LC_MESSAGES/django.po
index e27a878..6d2246a 100644
--- a/taggit/locale/he/LC_MESSAGES/django.po
+++ b/taggit/locale/he/LC_MESSAGES/django.po
@@ -66,4 +66,3 @@ msgstr ""
 #: contrib/suggest/models.py:57
 msgid "Enter a valid Regular Expression. To make it case-insensitive include \"(?i)\" in your expression."
 msgstr ""
-
diff --git a/taggit/locale/nl/LC_MESSAGES/django.po b/taggit/locale/nl/LC_MESSAGES/django.po
index 7871b0b..447ca7d 100644
--- a/taggit/locale/nl/LC_MESSAGES/django.po
+++ b/taggit/locale/nl/LC_MESSAGES/django.po
@@ -61,4 +61,3 @@ msgstr "Objecten getagged"
 #: contrib/suggest/models.py:57
 msgid "Enter a valid Regular Expression. To make it case-insensitive include \"(?i)\" in your expression."
 msgstr "Voer een valide reguliere expressie in. Voeg \"(?i)\" aan de expressie toe om deze hoofdletter ongevoelig te maken."
-
diff --git a/taggit/locale/ru/LC_MESSAGES/django.po b/taggit/locale/ru/LC_MESSAGES/django.po
index 42e3ebe..6629cba 100644
--- a/taggit/locale/ru/LC_MESSAGES/django.po
+++ b/taggit/locale/ru/LC_MESSAGES/django.po
@@ -67,4 +67,3 @@ msgstr "Элементы с меткой"
 #: contrib/suggest/models.py:57
 msgid "Enter a valid Regular Expression. To make it case-insensitive include \"(?i)\" in your expression."
 msgstr "Введите регулярное выражение. Чтобы сделать его чувствительным к регистру укажите \"(?i)\"."
-
diff --git a/taggit/managers.py b/taggit/managers.py
index 2bab18f..bb2ccd4 100644
--- a/taggit/managers.py
+++ b/taggit/managers.py
@@ -4,18 +4,30 @@ from operator import attrgetter
 
 from django import VERSION
 from django.contrib.contenttypes.models import ContentType
+from django.conf import settings
 from django.db import models, router
 from django.db.models.fields import Field
 from django.db.models.fields.related import (add_lazy_relation, ManyToManyRel,
-                                             RelatedField)
-from django.db.models.related import RelatedObject
+                                             OneToOneRel, RelatedField)
+
+if VERSION < (1, 8):
+    # related.py was removed in Django 1.8
+
+    # Depending on how Django was updated, related.py could still exist
+    # on the users system even on Django 1.8+, so we check the Django
+    # version before importing it to make sure this doesn't get imported
+    # accidentally.
+    from django.db.models.related import RelatedObject
+else:
+    RelatedObject = None
+
 from django.utils import six
 from django.utils.text import capfirst
 from django.utils.translation import ugettext_lazy as _
 
 from taggit.forms import TagField
 from taggit.models import GenericTaggedItemBase, TaggedItem
-from taggit.utils import require_instance_manager
+from taggit.utils import _get_field, require_instance_manager
 
 try:
     from django.contrib.contenttypes.fields import GenericRelation
@@ -23,10 +35,12 @@ except ImportError:  # django < 1.7
     from django.contrib.contenttypes.generic import GenericRelation
 
 try:
-    from django.db.models.related import PathInfo
-except ImportError:
-    pass  # PathInfo is not used on Django < 1.6
-
+    from django.db.models.query_utils import PathInfo
+except ImportError:  # Django < 1.8
+    try:
+        from django.db.models.related import PathInfo
+    except ImportError:
+        pass  # PathInfo is not used on Django < 1.6
 
 
 def _model_name(model):
@@ -45,6 +59,7 @@ class TaggableRel(ManyToManyRel):
         self.multiple = True
         self.through = None if VERSION < (1, 7) else through
         self.field = field
+        self.through_fields = None
 
     def get_joining_columns(self):
         return self.field.get_reverse_joining_columns()
@@ -88,11 +103,12 @@ class _TaggableManager(models.Manager):
     def is_cached(self, instance):
         return self.prefetch_cache_name in instance._prefetched_objects_cache
 
-    def get_queryset(self):
+    def get_queryset(self, extra_filters=None):
         try:
             return self.instance._prefetched_objects_cache[self.prefetch_cache_name]
         except (AttributeError, KeyError):
-            return self.through.tags_for(self.model, self.instance)
+            kwargs = extra_filters if extra_filters else {}
+            return self.through.tags_for(self.model, self.instance, **kwargs)
 
     def get_prefetch_queryset(self, instances, queryset=None):
         if queryset is not None:
@@ -113,7 +129,7 @@ class _TaggableManager(models.Manager):
         source_col = fk.column
         connection = connections[db]
         qn = connection.ops.quote_name
-        qs = self.get_queryset().using(db)._next_is_sticky().filter(**query).extra(
+        qs = self.get_queryset(query).using(db).extra(
             select={
                 '_prefetch_related_val': '%s.%s' % (qn(join_table), qn(source_col))
             }
@@ -144,14 +160,30 @@ class _TaggableManager(models.Manager):
                 raise ValueError("Cannot add {0} ({1}). Expected {2} or str.".format(
                     t, type(t), type(self.through.tag_model())))
 
-        # If str_tags has 0 elements Django actually optimizes that to not do a
-        # query.  Malcolm is very smart.
-        existing = self.through.tag_model().objects.filter(
-            name__in=str_tags
-        )
+        if getattr(settings, 'TAGGIT_CASE_INSENSITIVE', False):
+            # Some databases can do case-insensitive comparison with IN, which
+            # would be faster, but we can't rely on it or easily detect it.
+            existing = []
+            tags_to_create = []
+
+            for name in str_tags:
+                try:
+                    tag = self.through.tag_model().objects.get(name__iexact=name)
+                    existing.append(tag)
+                except self.through.tag_model().DoesNotExist:
+                    tags_to_create.append(name)
+        else:
+            # If str_tags has 0 elements Django actually optimizes that to not do a
+            # query.  Malcolm is very smart.
+            existing = self.through.tag_model().objects.filter(
+                name__in=str_tags
+            )
+
+            tags_to_create = str_tags - set(t.name for t in existing)
+
         tag_objs.update(existing)
 
-        for new_tag in str_tags - set(t.name for t in existing):
+        for new_tag in tags_to_create:
             tag_objs.add(self.through.tag_model().objects.create(name=new_tag))
 
         for tag in tag_objs:
@@ -199,7 +231,7 @@ class _TaggableManager(models.Manager):
         if len(lookup_keys) == 1:
             # Can we do this without a second query by using a select_related()
             # somehow?
-            f = self.through._meta.get_field_by_name(lookup_keys[0])[0]
+            f = _get_field(self.through, lookup_keys[0])
             objs = f.rel.to._default_manager.filter(**{
                 "%s__in" % f.rel.field_name: [r["content_object"] for r in qs]
             })
@@ -225,20 +257,42 @@ class _TaggableManager(models.Manager):
             results.append(obj)
         return results
 
+    # _TaggableManager needs to be hashable but BaseManagers in Django 1.8+ overrides
+    # the __eq__ method which makes the default __hash__ method disappear.
+    # This checks if the __hash__ attribute is None, and if so, it reinstates the original method.
+    if models.Manager.__hash__ is None:
+        __hash__ = object.__hash__
+
 
 class TaggableManager(RelatedField, Field):
+    # Field flags
+    many_to_many = True
+    many_to_one = False
+    one_to_many = False
+    one_to_one = False
+
     _related_name_counter = 0
 
     def __init__(self, verbose_name=_("Tags"),
                  help_text=_("A comma-separated list of tags."),
                  through=None, blank=False, related_name=None, to=None,
                  manager=_TaggableManager):
-        Field.__init__(self, verbose_name=verbose_name, help_text=help_text,
-                       blank=blank, null=True, serialize=False)
+
         self.through = through or TaggedItem
-        self.rel = TaggableRel(self, related_name, self.through, to=to)
         self.swappable = False
         self.manager = manager
+
+        rel = TaggableRel(self, related_name, self.through, to=to)
+
+        Field.__init__(
+            self,
+            verbose_name=verbose_name,
+            help_text=help_text,
+            blank=blank,
+            null=True,
+            serialize=False,
+            rel=rel,
+        )
         # NOTE: `to` is ignored, only used via `deconstruct`.
 
     def __get__(self, instance, model):
@@ -298,6 +352,9 @@ class TaggableManager(RelatedField, Field):
             else:
                 self.post_through_setup(cls)
 
+    def get_internal_type(self):
+        return 'ManyToManyField'
+
     def __lt__(self, other):
         """
         Required contribute_to_class as Django uses bisect
@@ -307,13 +364,18 @@ class TaggableManager(RelatedField, Field):
         return False
 
     def post_through_setup(self, cls):
-        self.related = RelatedObject(cls, self.model, self)
+        if RelatedObject is not None:  # Django < 1.8
+            self.related = RelatedObject(cls, self.model, self)
+
         self.use_gfk = (
             self.through is None or issubclass(self.through, GenericTaggedItemBase)
         )
         if not self.rel.to:
             self.rel.to = self.through._meta.get_field("tag").rel.to
-        self.related = RelatedObject(self.through, cls, self)
+
+        if RelatedObject is not None:  # Django < 1.8
+            self.related = RelatedObject(self.through, cls, self)
+
         if self.use_gfk:
             tagged_items = GenericRelation(self.through)
             tagged_items.contribute_to_class(cls, 'tagged_items')
@@ -346,10 +408,10 @@ class TaggableManager(RelatedField, Field):
         return _model_name(self.model)
 
     def m2m_reverse_name(self):
-        return self.through._meta.get_field_by_name("tag")[0].column
+        return _get_field(self.through, 'tag').column
 
     def m2m_reverse_field_name(self):
-        return self.through._meta.get_field_by_name("tag")[0].name
+        return _get_field(self.through, 'tag').name
 
     def m2m_target_field_name(self):
         return self.model._meta.pk.name
@@ -387,7 +449,7 @@ class TaggableManager(RelatedField, Field):
             alias_to_join = rhs_alias
         else:
             alias_to_join = lhs_alias
-        extra_col = self.through._meta.get_field_by_name('content_type')[0].column
+        extra_col = _get_field(self.through, 'content_type').column
         content_type_ids = [ContentType.objects.get_for_model(subclass).pk for
                             subclass in _get_subclasses(self.model)]
         if len(content_type_ids) == 1:
@@ -406,8 +468,8 @@ class TaggableManager(RelatedField, Field):
     # This and all the methods till the end of class are only used in django >= 1.6
     def _get_mm_case_path_info(self, direct=False):
         pathinfos = []
-        linkfield1 = self.through._meta.get_field_by_name('content_object')[0]
-        linkfield2 = self.through._meta.get_field_by_name(self.m2m_reverse_field_name())[0]
+        linkfield1 = _get_field(self.through, 'content_object')
+        linkfield2 = _get_field(self.through, self.m2m_reverse_field_name())
         if direct:
             join1infos = linkfield1.get_reverse_path_info()
             join2infos = linkfield2.get_path_info()
@@ -422,8 +484,8 @@ class TaggableManager(RelatedField, Field):
         pathinfos = []
         from_field = self.model._meta.pk
         opts = self.through._meta
-        object_id_field = opts.get_field_by_name('object_id')[0]
-        linkfield = self.through._meta.get_field_by_name(self.m2m_reverse_field_name())[0]
+        object_id_field = _get_field(self.through, 'object_id')
+        linkfield = _get_field(self.through, self.m2m_reverse_field_name())
         if direct:
             join1infos = [PathInfo(self.model._meta, opts, [from_field], self.rel, True, False)]
             join2infos = linkfield.get_path_info()
@@ -448,12 +510,12 @@ class TaggableManager(RelatedField, Field):
 
     def get_joining_columns(self, reverse_join=False):
         if reverse_join:
-            return (("id", "object_id"),)
+            return ((self.model._meta.pk.column, "object_id"),)
         else:
-            return (("object_id", "id"),)
+            return (("object_id", self.model._meta.pk.column),)
 
     def get_extra_restriction(self, where_class, alias, related_alias):
-        extra_col = self.through._meta.get_field_by_name('content_type')[0].column
+        extra_col = _get_field(self.through, 'content_type').column
         content_type_ids = [ContentType.objects.get_for_model(subclass).pk
                             for subclass in _get_subclasses(self.model)]
         return ExtraJoinRestriction(related_alias, extra_col, content_type_ids)
@@ -463,8 +525,7 @@ class TaggableManager(RelatedField, Field):
 
     @property
     def related_fields(self):
-        return [(self.through._meta.get_field_by_name('object_id')[0],
-                 self.model._meta.pk)]
+        return [(_get_field(self.through, 'object_id'), self.model._meta.pk)]
 
     @property
     def foreign_related_fields(self):
@@ -473,9 +534,18 @@ class TaggableManager(RelatedField, Field):
 
 def _get_subclasses(model):
     subclasses = [model]
-    for f in model._meta.get_all_field_names():
-        field = model._meta.get_field_by_name(f)[0]
-        if (isinstance(field, RelatedObject) and
+    if VERSION < (1, 8):
+        all_fields = (_get_field(model, f) for f in model._meta.get_all_field_names())
+    else:
+        all_fields = model._meta.get_fields()
+    for field in all_fields:
+        # Django 1.8 +
+        if (not RelatedObject and isinstance(field, OneToOneRel) and
+                getattr(field.field.rel, "parent_link", None)):
+            subclasses.extend(_get_subclasses(field.related_model))
+
+        # < Django 1.8
+        if (RelatedObject and isinstance(field, RelatedObject) and
                 getattr(field.field.rel, "parent_link", None)):
             subclasses.extend(_get_subclasses(field.model))
     return subclasses
diff --git a/taggit/migrations/0001_initial.py b/taggit/migrations/0001_initial.py
index 564fb3c..d8413e4 100644
--- a/taggit/migrations/0001_initial.py
+++ b/taggit/migrations/0001_initial.py
@@ -14,9 +14,9 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='Tag',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('name', models.CharField(unique=True, max_length=100, verbose_name='Name')),
-                ('slug', models.SlugField(unique=True, max_length=100, verbose_name='Slug')),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('name', models.CharField(help_text='', unique=True, max_length=100, verbose_name='Name')),
+                ('slug', models.SlugField(help_text='', unique=True, max_length=100, verbose_name='Slug')),
             ],
             options={
                 'verbose_name': 'Tag',
@@ -27,10 +27,10 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='TaggedItem',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('object_id', models.IntegerField(verbose_name='Object id', db_index=True)),
-                ('content_type', models.ForeignKey(related_name='taggit_taggeditem_tagged_items', verbose_name='Content type', to='contenttypes.ContentType')),
-                ('tag', models.ForeignKey(related_name='taggit_taggeditem_items', to='taggit.Tag')),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('object_id', models.IntegerField(help_text='', verbose_name='Object id', db_index=True)),
+                ('content_type', models.ForeignKey(related_name='taggit_taggeditem_tagged_items', verbose_name='Content type', to='contenttypes.ContentType', help_text='')),
+                ('tag', models.ForeignKey(related_name='taggit_taggeditem_items', to='taggit.Tag', help_text='')),
             ],
             options={
                 'verbose_name': 'Tagged Item',
diff --git a/taggit/migrations/0002_auto_20150616_2121.py b/taggit/migrations/0002_auto_20150616_2121.py
new file mode 100644
index 0000000..012a16f
--- /dev/null
+++ b/taggit/migrations/0002_auto_20150616_2121.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('taggit', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterIndexTogether(
+            name='taggeditem',
+            index_together=set([('content_type', 'object_id')]),
+        ),
+    ]
diff --git a/taggit/models.py b/taggit/models.py
index e5f5aea..3ea6945 100644
--- a/taggit/models.py
+++ b/taggit/models.py
@@ -1,5 +1,6 @@
 from __future__ import unicode_literals
 
+import django
 from django.contrib.contenttypes.models import ContentType
 from django.db import IntegrityError, models, transaction
 from django.db.models.query import QuerySet
@@ -8,6 +9,13 @@ from django.utils.encoding import python_2_unicode_compatible
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext
 
+from taggit.utils import _get_field
+try:
+    from unidecode import unidecode
+except ImportError:
+    unidecode = lambda tag: tag
+
+
 try:
     from django.contrib.contenttypes.fields import GenericForeignKey
 except ImportError:  # django < 1.7
@@ -61,8 +69,11 @@ class TagBase(models.Model):
             except IntegrityError:
                 pass
             # Now try to find existing slugs with similar names
-            slugs = set(Tag.objects.filter(slug__startswith=self.slug)
-                                   .values_list('slug', flat=True))
+            slugs = set(
+                self.__class__._default_manager
+                .filter(slug__startswith=self.slug)
+                .values_list('slug', flat=True)
+            )
             i = 1
             while True:
                 slug = self.slugify(self.name, i)
@@ -76,7 +87,7 @@ class TagBase(models.Model):
             return super(TagBase, self).save(*args, **kwargs)
 
     def slugify(self, tag, i=None):
-        slug = default_slugify(tag)
+        slug = default_slugify(unidecode(tag))
         if i is not None:
             slug += "_%d" % i
         return slug
@@ -101,11 +112,11 @@ class ItemBase(models.Model):
 
     @classmethod
     def tag_model(cls):
-        return cls._meta.get_field_by_name("tag")[0].rel.to
+        return _get_field(cls, 'tag').rel.to
 
     @classmethod
     def tag_relname(cls):
-        return cls._meta.get_field_by_name('tag')[0].rel.related_name
+        return _get_field(cls, 'tag').rel.related_name
 
     @classmethod
     def lookup_kwargs(cls, instance):
@@ -127,14 +138,17 @@ class TaggedItemBase(ItemBase):
         abstract = True
 
     @classmethod
-    def tags_for(cls, model, instance=None):
+    def tags_for(cls, model, instance=None, **extra_filters):
+        kwargs = extra_filters or {}
         if instance is not None:
-            return cls.tag_model().objects.filter(**{
+            kwargs.update({
                 '%s__content_object' % cls.tag_relname(): instance
             })
-        return cls.tag_model().objects.filter(**{
+            return cls.tag_model().objects.filter(**kwargs)
+        kwargs.update({
             '%s__content_object__isnull' % cls.tag_relname(): False
-        }).distinct()
+        })
+        return cls.tag_model().objects.filter(**kwargs).distinct()
 
 
 class GenericTaggedItemBase(ItemBase):
@@ -172,13 +186,15 @@ class GenericTaggedItemBase(ItemBase):
             }
 
     @classmethod
-    def tags_for(cls, model, instance=None):
+    def tags_for(cls, model, instance=None, **extra_filters):
         ct = ContentType.objects.get_for_model(model)
         kwargs = {
             "%s__content_type" % cls.tag_relname(): ct
         }
         if instance is not None:
             kwargs["%s__object_id" % cls.tag_relname()] = instance.pk
+        if extra_filters:
+            kwargs.update(extra_filters)
         return cls.tag_model().objects.filter(**kwargs).distinct()
 
 
@@ -186,3 +202,7 @@ class TaggedItem(GenericTaggedItemBase, TaggedItemBase):
     class Meta:
         verbose_name = _("Tagged Item")
         verbose_name_plural = _("Tagged Items")
+        if django.VERSION >= (1, 5):
+            index_together = [
+                ["content_type", "object_id"],
+            ]
diff --git a/taggit/utils.py b/taggit/utils.py
index 162b7bd..775d16a 100644
--- a/taggit/utils.py
+++ b/taggit/utils.py
@@ -1,10 +1,18 @@
 from __future__ import unicode_literals
 
+from django import VERSION
 from django.utils import six
 from django.utils.encoding import force_text
 from django.utils.functional import wraps
 
 
+def _get_field(model, name):
+    if VERSION < (1, 8):
+        return model._meta.get_field_by_name(name)[0]
+    else:
+        return model._meta.get_field(name)
+
+
 def parse_tags(tagstring):
     """
     Parses tag input, with multiple word input being activated and
diff --git a/tests/__init__.pyc b/tests/__init__.pyc
index 682085f..7b06e2a 100644
Binary files a/tests/__init__.pyc and b/tests/__init__.pyc differ
diff --git a/tests/forms.pyc b/tests/forms.pyc
index 8e361af..c43fb17 100644
Binary files a/tests/forms.pyc and b/tests/forms.pyc differ
diff --git a/tests/migrations/0001_initial.py b/tests/migrations/0001_initial.py
index 46743af..9f14cf7 100644
--- a/tests/migrations/0001_initial.py
+++ b/tests/migrations/0001_initial.py
@@ -16,8 +16,8 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='Article',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('title', models.CharField(max_length=100)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('title', models.CharField(help_text='', max_length=100)),
             ],
             options={
             },
@@ -26,7 +26,7 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='CustomManager',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
                 ('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', help_text='A comma-separated list of tags.', verbose_name='Tags')),
             ],
             options={
@@ -36,7 +36,7 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='CustomPKFood',
             fields=[
-                ('name', models.CharField(max_length=50, serialize=False, primary_key=True)),
+                ('name', models.CharField(help_text='', max_length=50, serialize=False, primary_key=True)),
             ],
             options={
             },
@@ -45,7 +45,7 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='CustomPKPet',
             fields=[
-                ('name', models.CharField(max_length=50, serialize=False, primary_key=True)),
+                ('name', models.CharField(help_text='', max_length=50, serialize=False, primary_key=True)),
             ],
             options={
             },
@@ -54,8 +54,8 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='CustomPKHousePet',
             fields=[
-                ('custompkpet_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='tests.CustomPKPet')),
-                ('trained', models.BooleanField(default=False)),
+                ('custompkpet_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='tests.CustomPKPet', help_text='')),
+                ('trained', models.BooleanField(default=False, help_text='')),
             ],
             options={
             },
@@ -64,8 +64,8 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='DirectFood',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('name', models.CharField(max_length=50)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('name', models.CharField(help_text='', max_length=50)),
             ],
             options={
             },
@@ -74,8 +74,8 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='DirectPet',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('name', models.CharField(max_length=50)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('name', models.CharField(help_text='', max_length=50)),
             ],
             options={
             },
@@ -84,8 +84,8 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='DirectHousePet',
             fields=[
-                ('directpet_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='tests.DirectPet')),
-                ('trained', models.BooleanField(default=False)),
+                ('directpet_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='tests.DirectPet', help_text='')),
+                ('trained', models.BooleanField(default=False, help_text='')),
             ],
             options={
             },
@@ -94,8 +94,8 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='Food',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('name', models.CharField(max_length=50)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('name', models.CharField(help_text='', max_length=50)),
                 ('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', help_text='A comma-separated list of tags.', verbose_name='Tags')),
             ],
             options={
@@ -105,7 +105,7 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='Movie',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
                 ('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', help_text='A comma-separated list of tags.', verbose_name='Tags')),
             ],
             options={
@@ -116,7 +116,7 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='MultipleTags',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
             ],
             options={
             },
@@ -125,7 +125,7 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='MultipleTagsGFK',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
                 ('tags1', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', help_text='A comma-separated list of tags.', verbose_name='Tags')),
             ],
             options={
@@ -135,8 +135,8 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='OfficialFood',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('name', models.CharField(max_length=50)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('name', models.CharField(help_text='', max_length=50)),
             ],
             options={
             },
@@ -145,8 +145,8 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='OfficialPet',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('name', models.CharField(max_length=50)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('name', models.CharField(help_text='', max_length=50)),
             ],
             options={
             },
@@ -155,8 +155,8 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='OfficialHousePet',
             fields=[
-                ('officialpet_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='tests.OfficialPet')),
-                ('trained', models.BooleanField(default=False)),
+                ('officialpet_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='tests.OfficialPet', help_text='')),
+                ('trained', models.BooleanField(default=False, help_text='')),
             ],
             options={
             },
@@ -165,10 +165,10 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='OfficialTag',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('name', models.CharField(unique=True, max_length=100, verbose_name='Name')),
-                ('slug', models.SlugField(unique=True, max_length=100, verbose_name='Slug')),
-                ('official', models.BooleanField(default=False)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('name', models.CharField(help_text='', unique=True, max_length=100, verbose_name='Name')),
+                ('slug', models.SlugField(help_text='', unique=True, max_length=100, verbose_name='Slug')),
+                ('official', models.BooleanField(default=False, help_text='')),
             ],
             options={
                 'abstract': False,
@@ -178,32 +178,20 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='OfficialThroughModel',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('object_id', models.IntegerField(verbose_name='Object id', db_index=True)),
-                ('content_type', models.ForeignKey(related_name='tests_officialthroughmodel_tagged_items', verbose_name='Content type', to='contenttypes.ContentType')),
-                ('tag', models.ForeignKey(related_name='tagged_items', to='tests.OfficialTag')),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('object_id', models.IntegerField(help_text='', verbose_name='Object id', db_index=True)),
+                ('content_type', models.ForeignKey(related_name='tests_officialthroughmodel_tagged_items', verbose_name='Content type', to='contenttypes.ContentType', help_text='')),
+                ('tag', models.ForeignKey(related_name='tagged_items', to='tests.OfficialTag', help_text='')),
             ],
             options={
                 'abstract': False,
             },
             bases=(models.Model,),
         ),
-        migrations.AddField(
-            model_name='officialfood',
-            name='tags',
-            field=taggit.managers.TaggableManager(to='tests.OfficialTag', through='tests.OfficialThroughModel', help_text='A comma-separated list of tags.', verbose_name='Tags'),
-            preserve_default=True,
-        ),
-        migrations.AddField(
-            model_name='officialpet',
-            name='tags',
-            field=taggit.managers.TaggableManager(to='tests.OfficialTag', through='tests.OfficialThroughModel', help_text='A comma-separated list of tags.', verbose_name='Tags'),
-            preserve_default=True,
-        ),
         migrations.CreateModel(
             name='Parent',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
             ],
             options={
             },
@@ -212,23 +200,17 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='Child',
             fields=[
-                ('parent_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='tests.Parent')),
+                ('parent_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='tests.Parent', help_text='')),
             ],
             options={
             },
             bases=('tests.parent',),
         ),
-        migrations.AddField(
-            model_name='parent',
-            name='tags',
-            field=taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', help_text='A comma-separated list of tags.', verbose_name='Tags'),
-            preserve_default=True,
-        ),
         migrations.CreateModel(
             name='Pet',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('name', models.CharField(max_length=50)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('name', models.CharField(help_text='', max_length=50)),
             ],
             options={
             },
@@ -237,23 +219,17 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='HousePet',
             fields=[
-                ('pet_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='tests.Pet')),
-                ('trained', models.BooleanField(default=False)),
+                ('pet_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='tests.Pet', help_text='')),
+                ('trained', models.BooleanField(default=False, help_text='')),
             ],
             options={
             },
             bases=('tests.pet',),
         ),
-        migrations.AddField(
-            model_name='pet',
-            name='tags',
-            field=taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', help_text='A comma-separated list of tags.', verbose_name='Tags'),
-            preserve_default=True,
-        ),
         migrations.CreateModel(
             name='Photo',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
                 ('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', help_text='A comma-separated list of tags.', verbose_name='Tags')),
             ],
             options={
@@ -264,91 +240,82 @@ class Migration(migrations.Migration):
         migrations.CreateModel(
             name='TaggedCustomPKFood',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('content_object', models.ForeignKey(help_text='', to='tests.CustomPKFood')),
+                ('tag', models.ForeignKey(related_name='tests_taggedcustompkfood_items', to='taggit.Tag', help_text='')),
             ],
             options={
                 'abstract': False,
             },
             bases=(models.Model,),
         ),
-        migrations.AddField(
-            model_name='custompkfood',
-            name='tags',
-            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.TaggedCustomPKFood', help_text='A comma-separated list of tags.', verbose_name='Tags'),
-            preserve_default=True,
-        ),
-        migrations.AddField(
-            model_name='taggedcustompkfood',
-            name='content_object',
-            field=models.ForeignKey(to='tests.CustomPKFood'),
-            preserve_default=True,
-        ),
-        migrations.AddField(
-            model_name='taggedcustompkfood',
-            name='tag',
-            field=models.ForeignKey(related_name='tests_taggedcustompkfood_items', to='taggit.Tag'),
-            preserve_default=True,
-        ),
         migrations.CreateModel(
             name='TaggedCustomPKPet',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('content_object', models.ForeignKey(help_text='', to='tests.CustomPKPet')),
+                ('tag', models.ForeignKey(related_name='tests_taggedcustompkpet_items', to='taggit.Tag', help_text='')),
             ],
             options={
                 'abstract': False,
             },
             bases=(models.Model,),
         ),
-        migrations.AddField(
-            model_name='custompkpet',
-            name='tags',
-            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.TaggedCustomPKPet', help_text='A comma-separated list of tags.', verbose_name='Tags'),
-            preserve_default=True,
-        ),
-        migrations.AddField(
-            model_name='taggedcustompkpet',
-            name='content_object',
-            field=models.ForeignKey(to='tests.CustomPKPet'),
-            preserve_default=True,
-        ),
-        migrations.AddField(
-            model_name='taggedcustompkpet',
-            name='tag',
-            field=models.ForeignKey(related_name='tests_taggedcustompkpet_items', to='taggit.Tag'),
-            preserve_default=True,
-        ),
         migrations.CreateModel(
             name='TaggedFood',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('content_object', models.ForeignKey(help_text='', to='tests.DirectFood')),
+                ('tag', models.ForeignKey(related_name='tests_taggedfood_items', to='taggit.Tag', help_text='')),
             ],
             options={
                 'abstract': False,
             },
             bases=(models.Model,),
         ),
-        migrations.AddField(
-            model_name='directfood',
-            name='tags',
-            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.TaggedFood', help_text='A comma-separated list of tags.', verbose_name='Tags'),
-            preserve_default=True,
+        migrations.CreateModel(
+            name='TaggedPet',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('content_object', models.ForeignKey(help_text='', to='tests.DirectPet')),
+                ('tag', models.ForeignKey(related_name='tests_taggedpet_items', to='taggit.Tag', help_text='')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=(models.Model,),
         ),
-        migrations.AddField(
-            model_name='taggedfood',
-            name='content_object',
-            field=models.ForeignKey(to='tests.DirectFood'),
-            preserve_default=True,
+        migrations.CreateModel(
+            name='Through1',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('content_object', models.ForeignKey(help_text='', to='tests.MultipleTags')),
+                ('tag', models.ForeignKey(related_name='tests_through1_items', to='taggit.Tag', help_text='')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=(models.Model,),
         ),
-        migrations.AddField(
-            model_name='taggedfood',
-            name='tag',
-            field=models.ForeignKey(related_name='tests_taggedfood_items', to='taggit.Tag'),
-            preserve_default=True,
+        migrations.CreateModel(
+            name='Through2',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('content_object', models.ForeignKey(help_text='', to='tests.MultipleTags')),
+                ('tag', models.ForeignKey(related_name='tests_through2_items', to='taggit.Tag', help_text='')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=(models.Model,),
         ),
         migrations.CreateModel(
-            name='TaggedPet',
+            name='ThroughGFK',
             fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, help_text='', verbose_name='ID')),
+                ('object_id', models.IntegerField(help_text='', verbose_name='Object id', db_index=True)),
+                ('content_type', models.ForeignKey(related_name='tests_throughgfk_tagged_items', verbose_name='Content type', to='contenttypes.ContentType', help_text='')),
+                ('tag', models.ForeignKey(related_name='tagged_items', to='taggit.Tag', help_text='')),
             ],
             options={
                 'abstract': False,
@@ -356,61 +323,41 @@ class Migration(migrations.Migration):
             bases=(models.Model,),
         ),
         migrations.AddField(
-            model_name='directpet',
+            model_name='pet',
             name='tags',
-            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.TaggedPet', help_text='A comma-separated list of tags.', verbose_name='Tags'),
+            field=taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', help_text='A comma-separated list of tags.', verbose_name='Tags'),
             preserve_default=True,
         ),
         migrations.AddField(
-            model_name='taggedpet',
-            name='content_object',
-            field=models.ForeignKey(to='tests.DirectPet'),
+            model_name='parent',
+            name='tags',
+            field=taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', help_text='A comma-separated list of tags.', verbose_name='Tags'),
             preserve_default=True,
         ),
         migrations.AddField(
-            model_name='taggedpet',
-            name='tag',
-            field=models.ForeignKey(related_name='tests_taggedpet_items', to='taggit.Tag'),
+            model_name='officialpet',
+            name='tags',
+            field=taggit.managers.TaggableManager(to='tests.OfficialTag', through='tests.OfficialThroughModel', help_text='A comma-separated list of tags.', verbose_name='Tags'),
             preserve_default=True,
         ),
-        migrations.CreateModel(
-            name='Through1',
-            fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-            ],
-            options={
-                'abstract': False,
-            },
-            bases=(models.Model,),
-        ),
         migrations.AddField(
-            model_name='multipletags',
-            name='tags1',
-            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.Through1', help_text='A comma-separated list of tags.', verbose_name='Tags'),
+            model_name='officialfood',
+            name='tags',
+            field=taggit.managers.TaggableManager(to='tests.OfficialTag', through='tests.OfficialThroughModel', help_text='A comma-separated list of tags.', verbose_name='Tags'),
             preserve_default=True,
         ),
         migrations.AddField(
-            model_name='through1',
-            name='content_object',
-            field=models.ForeignKey(to='tests.MultipleTags'),
+            model_name='multipletagsgfk',
+            name='tags2',
+            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.ThroughGFK', help_text='A comma-separated list of tags.', verbose_name='Tags'),
             preserve_default=True,
         ),
         migrations.AddField(
-            model_name='through1',
-            name='tag',
-            field=models.ForeignKey(related_name='tests_through1_items', to='taggit.Tag'),
+            model_name='multipletags',
+            name='tags1',
+            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.Through1', help_text='A comma-separated list of tags.', verbose_name='Tags'),
             preserve_default=True,
         ),
-        migrations.CreateModel(
-            name='Through2',
-            fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-            ],
-            options={
-                'abstract': False,
-            },
-            bases=(models.Model,),
-        ),
         migrations.AddField(
             model_name='multipletags',
             name='tags2',
@@ -418,34 +365,27 @@ class Migration(migrations.Migration):
             preserve_default=True,
         ),
         migrations.AddField(
-            model_name='through2',
-            name='content_object',
-            field=models.ForeignKey(to='tests.MultipleTags'),
+            model_name='directpet',
+            name='tags',
+            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.TaggedPet', help_text='A comma-separated list of tags.', verbose_name='Tags'),
             preserve_default=True,
         ),
         migrations.AddField(
-            model_name='through2',
-            name='tag',
-            field=models.ForeignKey(related_name='tests_through2_items', to='taggit.Tag'),
+            model_name='directfood',
+            name='tags',
+            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.TaggedFood', help_text='A comma-separated list of tags.', verbose_name='Tags'),
             preserve_default=True,
         ),
-        migrations.CreateModel(
-            name='ThroughGFK',
-            fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('object_id', models.IntegerField(verbose_name='Object id', db_index=True)),
-                ('content_type', models.ForeignKey(related_name='tests_throughgfk_tagged_items', verbose_name='Content type', to='contenttypes.ContentType')),
-                ('tag', models.ForeignKey(related_name='tagged_items', to='taggit.Tag')),
-            ],
-            options={
-                'abstract': False,
-            },
-            bases=(models.Model,),
+        migrations.AddField(
+            model_name='custompkpet',
+            name='tags',
+            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.TaggedCustomPKPet', help_text='A comma-separated list of tags.', verbose_name='Tags'),
+            preserve_default=True,
         ),
         migrations.AddField(
-            model_name='multipletagsgfk',
-            name='tags2',
-            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.ThroughGFK', help_text='A comma-separated list of tags.', verbose_name='Tags'),
+            model_name='custompkfood',
+            name='tags',
+            field=taggit.managers.TaggableManager(to='taggit.Tag', through='tests.TaggedCustomPKFood', help_text='A comma-separated list of tags.', verbose_name='Tags'),
             preserve_default=True,
         ),
         migrations.CreateModel(
diff --git a/tests/migrations/0001_initial.pyc b/tests/migrations/0001_initial.pyc
new file mode 100644
index 0000000..5857bc3
Binary files /dev/null and b/tests/migrations/0001_initial.pyc differ
diff --git a/tests/migrations/__init__.pyc b/tests/migrations/__init__.pyc
new file mode 100644
index 0000000..828706c
Binary files /dev/null and b/tests/migrations/__init__.pyc differ
diff --git a/tests/models.pyc b/tests/models.pyc
index 6a54db0..fe664c3 100644
Binary files a/tests/models.pyc and b/tests/models.pyc differ
diff --git a/tests/tests.py b/tests/tests.py
index 791ac77..157fbeb 100644
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -6,7 +6,9 @@ import django
 from django.contrib.contenttypes.models import ContentType
 from django.core import serializers
 from django.core.exceptions import ImproperlyConfigured, ValidationError
+from django.db import connection
 from django.test import TestCase, TransactionTestCase
+from django.test.utils import override_settings
 from django.utils.encoding import force_text
 
 from .forms import CustomPKFoodForm, DirectFoodForm, FoodForm, OfficialFoodForm
@@ -336,7 +338,14 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
         self.assertTrue(hasattr(field, 'rel'))
         self.assertTrue(hasattr(field.rel, 'to'))
         self.assertTrue(hasattr(field, 'related'))
-        self.assertEqual(self.food_model, field.related.model)
+
+        # This API has changed in Django 1.8
+        # https://code.djangoproject.com/ticket/21414
+        if django.VERSION >= (1, 8):
+            self.assertEqual(self.food_model, field.model)
+            self.assertEqual(self.tag_model, field.related.model)
+        else:
+            self.assertEqual(self.food_model, field.related.model)
 
     def test_names_method(self):
         apple = self.food_model.objects.create(name="apple")
@@ -368,6 +377,27 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
                 'apple': set(['1', '2'])
             })
 
+    def test_internal_type_is_manytomany(self):
+        self.assertEqual(
+            TaggableManager().get_internal_type(), 'ManyToManyField'
+        )
+
+    def test_prefetch_no_extra_join(self):
+        apple = self.food_model.objects.create(name="apple")
+        apple.tags.add('1', '2')
+        with self.assertNumQueries(2):
+            l = list(self.food_model.objects.prefetch_related('tags').all())
+            join_clause = 'INNER JOIN "%s"' % self.taggeditem_model._meta.db_table
+            self.assertEqual(connection.queries[-1]['sql'].count(join_clause), 1, connection.queries[-2:])
+
+    @override_settings(TAGGIT_CASE_INSENSITIVE=True)
+    def test_with_case_insensitive_option(self):
+        spain = self.tag_model.objects.create(name="Spain", slug="spain")
+        orange = self.food_model.objects.create(name="orange")
+        orange.tags.add('spain')
+        self.assertEqual(list(orange.tags.all()), [spain])
+
+
 class TaggableManagerDirectTestCase(TaggableManagerTestCase):
     food_model = DirectFood
     pet_model = DirectPet
diff --git a/tests/tests.pyc b/tests/tests.pyc
index ec001fc..e6933c2 100644
Binary files a/tests/tests.pyc and b/tests/tests.pyc differ
diff --git a/tox.ini b/tox.ini
index 7d0ef42..ad6b3df 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,13 +4,15 @@ usedevelop = True
 deps =
 	flake8
 deps14 =
-	https://github.com/django/django/archive/stable/1.4.x.zip#egg=django
+	https://github.com/django/django/archive/stable/1.4.x.tar.gz#egg=django
 deps15 =
-	https://github.com/django/django/archive/stable/1.5.x.zip#egg=django
+	https://github.com/django/django/archive/stable/1.5.x.tar.gz#egg=django
 deps16 =
-	https://github.com/django/django/archive/stable/1.6.x.zip#egg=django
+	https://github.com/django/django/archive/stable/1.6.x.tar.gz#egg=django
 deps17 =
-	https://github.com/django/django/archive/stable/1.7.x.zip#egg=django
+	https://github.com/django/django/archive/stable/1.7.x.tar.gz#egg=django
+deps18 =
+	https://github.com/django/django/archive/stable/1.8.x.tar.gz#egg=django
 
 commands =
 	python ./runtests.py {posargs}
@@ -58,6 +60,12 @@ deps =
 	{[testenv]deps}
 	{[testenv]deps17}
 
+[testenv:py27-1.8.x]
+basepython = python2.7
+deps =
+	{[testenv]deps}
+	{[testenv]deps18}
+
 [testenv:py33-1.5.x]
 basepython = python3.3
 deps =
@@ -76,6 +84,12 @@ deps =
 	{[testenv]deps}
 	{[testenv]deps17}
 
+[testenv:py33-1.8.x]
+basepython = python3.3
+deps =
+	{[testenv]deps}
+	{[testenv]deps18}
+
 [testenv:py34-1.5.x]
 basepython = python3.4
 deps =
@@ -93,3 +107,9 @@ basepython = python3.4
 deps =
 	{[testenv]deps}
 	{[testenv]deps17}
+
+[testenv:py34-1.8.x]
+basepython = python3.4
+deps =
+	{[testenv]deps}
+	{[testenv]deps18}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/collab-maint/django-taggit.git



More information about the Python-modules-commits mailing list